MyBatis好好学六(常见问题和解决方案)

MyBatis好好学六(常见问题和解决方案)

1、mybatis分页方式

-- 原始方法(mysql数据库)
select * from table limit 5; --返回前5行

select * from table limit 0,5; --同上,返回前5行

select * from table limit 5,10; --返回6-15行 
-- 原始方法(oracle数据库可以使用 rownum) 
SELECT * FROM 
(SELECT ROWNUM R,t1.* From Sys_option where rownum < 30 ) t2
Where t2.R >= 10  
从第十行开始算,返回20条记录


-- 拦截StatementHandler,其实质还是在最后生成limit语句
详见:www.cnblogs.com/jcli/archive/2011/08/09/2132222.html 

-- 使用PageHelper插件,这是目前比较常见的方法:
原文:www.cnblogs.com/digdeep/p/4608933.html

2、# #{}和${}区别

#{} 是预编译处理,像传进来的数据会加个" "(#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号)

${} 就是字符串替换。直接替换掉占位符。$方式一般用于传入数据库对象,例如传入表名.
使用 ${} 的话会导致 sql 注入。什么是 SQL 注入呢?比如 select * from user where id = ${value}
value 应该是一个数值吧。然后如果对方传过来的是 001  and name = tom。这样不就相当于多加了一个条件嘛?
把SQL语句直接写进来了。如果是攻击性的语句呢?001;drop table user,直接把表给删了

所以为了防止 SQL 注入,能用 #{} 的不要去用 ${}
如果非要用 ${} 的话,那要注意防止 SQL 注入问题,可以手动判定传入的变量,进行过滤,一般 SQL 注入会输入很长的一条 SQL 语句

3、MyBatis接口绑定的几种方式

接口绑定有两种方式

1、使用注解,在接口的方法上面添加@Select@Update等注解,里面写上对应的SQL语句进行SQL语句的绑定。

2、通过映射文件xml方式进行绑定,指定xml映射文件中的namespace对应的接口的全路径名

4、MyBatis模糊查询

-- 方式1 ($  这种方式,简单,但是无法防止SQL注入)
LIKE  '%${name}%'
-- 方式2 (#)
like "%"#{name}"%"
-- 方式3(concat函数)
AND name LIKE CONCAT(CONCAT('%',#{name},'%')) 

-- 方式4 (bind标签)
<select id="searchStudents" resultType="com.example.entity.StudentEntity"
  parameterType="com.example.entity.StudentEntity">
  <bind name="pattern1" value="'%' + _parameter.name + '%'" />
  <bind name="pattern2" value="'%' + _parameter.address + '%'" />
  SELECT * FROM test_student
  <where>
   <if test="age != null and age != '' and compare != null and compare != ''">
    age
    ${compare}
    #{age}
   </if>
   <if test="name != null and name != ''">
    AND name LIKE #{pattern1}
   </if>
   <if test="address != null and address != ''">
    AND address LIKE #{pattern2}
   </if>
  </where>
  ORDER BY id
 </select> 

-- 方式5 (硬编码)
param.setUsername("%CD%"); 在 java 代码中传参的时候直接写上
<if test="username!=null"> AND username LIKE #{username}</if>

5、# mapper.XML对应一个DAO接口,DAO是否可以重载?

        不能重载,方法名对应的 mapper.xml 文件里的一个 id,这个与方法名对应,系统会根据 namespace+id 找到对应的方法对应。

Dao 接口即 Mapper 接口。接口的全限名,就是映射文件中的 namespace 的值;接口的方法名,
就是映射文件中 Mapper 的 Statement 的 id 值;
接口方法内的参数,就是传递给 sql 的参数。Mapper 接口是没有实现类的,当调用接口方法时,
接口全限名+方法名拼接字符串作为 key 值,可唯一定位一个 MapperStatement。
在 Mybatis 中,每一个、、、标签,都会被解析为一个MapperStatement 对象。

        Mapper 接口里的方法,是不能重载的,因为是使用 全限名+方法名 的保存和寻找策略。Mapper 接口的工作原理是 JDK 动态代理,Mybatis 运行时会使用 JDK动态代理为 Mapper 接口生成代理对象 proxy,代理对象会拦截接口方法,转而执行 MapperStatement 所代表的 sql,然后将 sql 执行结果返回。

6、 A标签引用B标签,B标签可以定义在A标签后面吗?

        虽然 Mybatis 解析 Xml 映射文件是按照顺序解析的,但是,被引用的 B 标签依然可以定义在任何地方,Mybatis 都可以正确识别。

        Mybatis 解析 A 标签时,发现引用了 B 标签,未解析到 B 标签,此时会把 A 标签标记为未解析状态;继续解析下面内容,把剩下解析完之后,再解析标记为未解析的标签;

此时已解析到 B 标签,此时再解析A标签时,B标签已经存在,A 标签也就顺利解析完成。

7、不同映射文件中的id是否可以重复?

        可以重复,但是需要映射文件的namespace不同。

8、MyBatis是否可以映射到枚举类

        Mybatis 可以映射枚举类。

        不单可以映射枚举类,Mybatis 可以映射任何对象到表的一列上。映射方式为自定义一个 TypeHandler ,实现 TypeHandler 的 ​setParameter()​ 和 ​getResult()​ 接口方法。 

        TypeHandler 有两个作用,一是完成从 javaType 至 jdbcType 的转换,二是完成 jdbcType 至 javaType 的转换,体现为 ​setParameter() ​和 ​getResult()​ 两个方法,分别代表设置 sql 问号占位符参数和获取列查询结果。

9、# MyBatis如何获取自动生成的主键id

-- MySQL:Mapper 文件 insert 语句设置    
<insert id="insertname" useGeneratedKeys="true" keyProperty="id">
    insert into names (name) values (#{name})
</insert> 

-- Oracle:Mapper 文件 insert 语句增加
<insert id="insertStudent" parameterType="org.school.entity.Student">
    insert into student(name,age,sex) value(#{name},#{age},#{birthday},#{sex})
        <selectKey resultType="Long" keyProperty="id">
              select Max(id) from student
        </selectKey>
</insert> 
针对 Oracle 数据库,还可以使用@SelectKey 注解来为任意SQL语句来指定主键值,作为主键列的值。
resultType="Long" 指定了返回值的类型;
keyProperty="id" 指定了要将自动生成的主键值赋给 id 属性;
select Max(id) from student 即选择当前表中最大的id作为主键id,实现了自增效果


-- 获取序列号(oracle)
-- 创建sequence
CREATE SEQUENCE DATAGOVSERVER.T_IAM_REFRENCE_DATA_ID_SEQ INCREMENT 1 MINVALUE 1 START 2 NO CACHE  NO CYCLE;
-- dao接口
@Select("select ${sequenceName}.NEXTVAL FROM DUAL")
Long getSequenceId(@Param("sequenceName")String sequenceName);

10、MyBatis传递多个参数

-- 使用map接口传递参数
-- 使用注解传递多个参数注解@Param(org.apache.ibatis.annotations.Param)
-- 通过Java Bean传递多个参数

11、MyBatis缓存机制

  • 一级缓存

        一级缓存为 ​SqlSession​ 缓存,缓存的数据只在 SqlSession 内有效。在操作数据库的时候需要先创建 SqlSession 会话对象,在对象中有一个 HashMap 用于存储缓存数据,此 HashMap 是当前会话对象私有的,别的 SqlSession 会话对象无法访问。

        具体流程:

        第一次执行 select 完毕会将查到的数据写入 SqlSession 内的 HashMap 中缓存起来

        第二次执行 select 会从缓存中查数据,如果 select 同传参数一样,那么就能从缓存中返回数据,不用去数据库了,从而提高了效率

注意:

        1、如果 SqlSession 执行了 DML 操作(insert、update、delete),并 commit 了,那么 mybatis 就会清空当前 SqlSession 缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现差异

        2、当一个 SqlSession 结束后那么他里面的一级缓存也就不存在了, mybatis 默认是开启一级缓存,不需要配置

        3、 mybatis 的缓存是基于 [namespace:sql语句:参数] 来进行缓存的,意思就是, SqlSession 的 HashMap 存储缓存数据时,是使用 [namespace:sql:参数] 作为 key ,查询返回的语句作为 value 保存的

  • 二级缓存

二级缓存是​ mapper​ 级别的缓存,也就是同一个 namespace 的 mapper.xml ,当多个 SqlSession 使用同一个 Mapper 操作数据库的时候,得到的数据会缓存在同一个二级缓存区域

二级缓存默认是没有开启的。需要在 setting 全局参数中配置开启二级缓存

开启二级缓存步骤:

1、​conf.xml​ 配置全局变量开启二级缓存

<settings>
    <setting name="cacheEnabled" value="true"/>默认是false:关闭二级缓存
<settings>

2、在​ userMapper.xml ​中配置

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>当前mapper下所有语句开启二级缓存

        这里配置了一个 ​FIFO ​缓存,并每隔60秒刷新,最大存储512个对象,而返回的对象是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。可用的收回策略有,默认的是​LRU​:

  1. LRU ​- 最近最少使用的;移除最长时间不被使用的对象。 
  2. FIFO ​- 先进先出;按对象进入缓存的顺序来移除它们。 
  3. SOFT​- 软引用;移除基于垃圾回收器状态和软引用规则的对象 
  4. WEAK ​- 弱引用;更积极地移除基干垃圾收集器状态和弱引用规则的对象

若想禁用当前​select​语句的二级缓存,添加 ​useCache="false"​修改如下:

<select id="getCountByName" parameterType="java.util.Map" resultType="INTEGER" statementType="CALLABLE" useCache="false">

具体流程:

        1.当一个​ sqlseesion ​执行了一次​ select​ 后,在关闭此​ session​ 的时候,会将查询结果缓存到二级缓存

        2.当另一个​ sqlsession ​执行​ select​ 时,首先会在他自己的一级缓存中找,如果没找到,就回去二级缓存中找,找到了就返回,就不用去数据库了,从而减少了数据库压力提高了性能

注意:

        1、如果 ​SqlSession​ 执行了 DML 操作​(insert、update、delete)​,并 ​commit​ 了,那么 ​mybatis​ 就会清空当前​ mapper​ 缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现差异

        2、​ mybatis​ 的一级缓存是基于​ [namespace:sql语句:参数] ​来进行缓存的,意思就是,​SqlSession​ 的 ​HashMap​ 存储缓存数据时,是使用 ​[namespace:sql:参数] ​作为 ​key​ ,查询返回的语句作为 ​value​ 保存的。

12、MyBatis时间timestamp做条件进行查询

        首先要将条件 转换为 时间戳:

long startTime = TimeUtil.parseTimestamp(start);
long endTime = TimeUtil.parseTimestamp(end);

/*对应工具类*/
public static long parseTimestamp(String datetime){
    try{
    SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date date = dateformat.parse(datetime);
    return date.getTime()/1000;            
    }catch(Exception e){
        e.printStackTrace();
    }
    return 0;
}

然后Mapper.xml中  使用BETWEEN and  和 to_timestamp:

<if test="startDate !=null and startDate !='' and endDate !=null and endDate !=''">
   AND tdnm.create_time BETWEEN to_timestamp(#{startDate}) AND to_timestamp(#{endDate})
</if>
end
  • 作者:旭仔(联系作者)
  • 发表时间:2024-03-10 20:40
  • 版权声明:自由转载-非商用-非衍生-保持署名
  • 转载声明:如果是转载栈主转载的文章,请附上原文链接
  • 公众号转载:请在文末添加作者公众号二维码(公众号二维码见右边,欢迎关注)
  • 评论