Mybatis总结
动态SQL的使用
- 动态SQL是Mybatis强大的特性之一,极大的简化我们拼装SQL的操作
- 动态SQL元素和使用JSTL或其他类似基于XML的文本处理器相似
- MySQL采用功能强大的OGNL的表达式来简化操作
- if
- choose(when,otherwise)
- trim(where,set)
- foreach
if标签
- 与java中的if类似,其中if标签中的test代表条件,支持使用& 和 ||,同时支持trim()
<select id="getEmpsByConditionIf" resultType="com.wjl.mybatis.bean.Employee">
select * from tbl_employee where
<if test="id != null">
id=#{id}
</if>
<if test="lastName != null and lastName !=''">
and last_name like #{lastName}
</if>
<if test="email !=null and email.trim()!=''">
and email = #{email}
</if>
<if test="gender ==0 || gender ==1">
and gender = #{gender}
</if>
</select>
- where标签,当如上图一样使用if时,当id为空时,此时sql语句编程 select * from tbl_employee where and last_name = xxx …,where 标签,将if包含,会将if标签中多余的and或者or去除(只能放到前面and email = #{email}) where只会去掉第一个多出来的,where标签代表使用where查询
<select id="getEmpsByConditionIf" resultType="com.wjl.mybatis.bean.Employee">
select * from tbl_employee
<where>
<if test="id != null">
id=#{id}
</if>
<if test="lastName != null and lastName !=''">
and last_name like #{lastName}
</if>
<if test="email !=null and email.trim()!=''">
and email = #{email}
</if>
<if test="gender ==0 || gender ==1">
and gender = #{gender}
</if>
</where>
</select>
trim标签
- 当有人习惯将and写在之后时,如下,这样where标签就无法满足去除掉多余的and的功能了,此时可以使用trim标签来自定义截取字符串
<if test="id != null">
id=#{id} and
</if>
<select id="getEmpsByConditionTrim" resultType="com.wjl.mybatis.bean.Employee">
select * from tbl_employee
<!--prefix="" suffix="" prefixOverrides="" suffixOverrides=""
prefix 前缀:trim标签体中是整个字符串中拼串后的结果,prefix是给整个字符串加一个前缀
prefixOverrides : 去掉拼接字符串前多余的字符
suffix:整体加后缀
suffixOverrides:去除多余的后面的字符
使用trim来自定义字符串截取规则
-->
<trim prefix="where" suffixOverrides="and">
<if test="id != null">
id=#{id} and
</if>
<if test="lastName != null and lastName !=''">
last_name like #{lastName} and
</if>
<if test="email !=null and email.trim()!=''">
email = #{email} and
</if>
<if test="gender ==0 || gender ==1">
gender = #{gender}
</if>
</trim>
</select>
choose标签
- 类似于java语言中的分支选择:swtich-case,分支选择
<select id="getEmpsByConditionChoose" resultType="com.wjl.mybatis.bean.Employee">
select * from tbl_employee
<where>
<choose>
<when test="id!=null">id = #{id}</when>
<when test="lastName!=null">last_name =#{lastName}</when>
<when test="email!=null">email=#{email}</when>
<otherwise>
gender=0
</otherwise>
</choose>
</where>
</select>
foreach标签
- 类似于Java中的for循环,collection:指定输入对象中集合属性 item:每次遍历生成的对象 open:开始遍历时拼接的串 close:结束遍历时两个对象需要拼接的串,separator mybatis会在每次迭代后给sql语句append上separator属性指定的字符,该参数为可选项,index 在list、Set和数组中,index表示当前迭代的位置,在map中,index代指是元素的key,该参数是可选项。
<insert id="insertEmployee">
insert into tbl_employee(last_name,email,gender)
values
<foreach collection="list" item="item" index="index" separator=",">
(#{item.lastName,jdbcType=VARCHAR}, #{item.email,jdbcType=VARCHAR}, #{item.gender,jdbcType=VARCHAR})
</foreach>
</insert>
Mybatis的缓存机制
- Mybatis包含了一个非常强大的查询缓存特性,它可以非常方便的配置和定制,可以极大的提升查询的效率
- MyBatis系统中默认定义了两级缓存,分别为一级缓存和二级缓存
- 默认情况下只有一级缓存开启(SqlSession级别的缓存,也称为本地缓存)开启.
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存
- 为了提高可扩展型.mybatis定义了缓存接口Cache,可以通过实现Cache接口来自定义二级缓存.
一级缓存
- 一级缓存(local cache),即本地缓存,作用域默认为sqlSession.当Session flush 或close后,该Session中的所有Cache将被清空
- 本地缓存不能被关闭,但可以调用clearCache()来清空本地缓存,或者改变缓存的作用域
- 同一次会话期间,只要查询过的数据都会保存在当前SqlSession的一个Map中
- key:hashCode+查询的sqlid+编写的sql查询语句+参数
- 下述例子,当进行第二次查询的时候,没有清空缓存的时候,两个查询出来的对象相等,并不会再次发送请求在数据库中查询,当手动清空缓存后
sqlSession.clearCache();
,会再次发送请求
@Test
public void testFirstLevelCache() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSeesionFactory();
SqlSession sqlSession = sqlSessionFactory.openSession(true);
try {
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee empById = mapper.getEmpById(1);
System.out.println(empById);
// 手动清空缓存
//sqlSession.clearCache();
Employee empById1 = mapper.getEmpById(1);
System.out.println(empById1 == empById);
} finally {
sqlSession.close();
}
}
- 一级缓存失效的四种情况(没有使用到当前一级缓存的情况,效果就是,还需要向数据库发出查询)
- sqlSession发生改变 System.out.println(empById1==empById); false
- sqlSession相同,查询条件不同(当前一级缓存中没有这个数据)
- sqlSession相同,两次查询之间执行了增删改(这次增删改可能会对当前数据有影响)
- sqlSession相同,两次查询期间手动清空了缓存
二级缓存
- 二级缓存(second level cache),全局作用缓存,二级缓存默认不开启,需要手动配置
- 二级缓存的工作机制
- 一个会话,查询一条数据,这个会话就会被放在当前会话的一级缓存中
- 如果会话关闭;一级缓存中的数据会被保存到二级缓存中,新的会话查询信息,就可以参照二级缓存
- sqlSession === EmployeeMapper==》Employee
DepartmentMapper ===》Department - 不同namespace查出的数据会放在自己对应的缓存中(map)
- 查出的数据都会放在一级缓存中,只有会话提交或者关闭以后,一级缓存的数据才会转移到二级缓存中
- Mybatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口
- 二级缓存在SqlSession关闭或者提交之后才会生效
- 使用步骤
-
开启全局二级缓存 ,在全局配置文件的settings中设置
<setting name="cacheEnabled" value="true"></setting>
<settings> <!-- 开启全局二级缓存 --> <setting name="cacheEnabled" value="true"></setting> </settings>
-
去对应Mapper.xml中配置使用二级缓存 ,
<cache></cache>
-
POJO需要实现序列化接口
-
测试
@Test public void testSecondLevelCache() throws IOException { SqlSessionFactory sqlSessionFactory = getSqlSeesionFactory(); SqlSession sqlSession = sqlSessionFactory.openSession(true); SqlSession sqlSession1 = sqlSessionFactory.openSession(true); try{ // EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); EmployeeMapper mapper1 = sqlSession1.getMapper(EmployeeMapper.class); Employee empById = mapper.getEmpById(1); System.out.println(empById); sqlSession.close(); // 第二次查询是从二级缓存中获取的,并没有发送数据 Employee empById1 = mapper1.getEmpById(1); System.out.println(empById1); sqlSession1.close(); }finally { } }
-
- 相关mapper.xml中配置
<cache></cache>
的相关属性- eviction 缓存的回收策略
- LRU-最近最少使用的:移除最长时间不被使用的对象
- FIFO:先进先出:按照对象进入缓存的顺序移除他们
- SOFT:软引用:移除基于垃圾回收器状态和软引用规则的对象
- WEAK:软引用:更积极的移除位于基于垃圾回收器状态和弱引用规则的对象
- 默认是LRU
- flushInterval: 缓存刷新间隔
- 多长时间清空一次,默认不清空,设置毫秒值
- readOnly:缓存是否只读
- true:只读,所有从缓存中获取数据的操作都是只读操作,不会修改数据, mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户,不安全,速度快
- false:非只读,mybatis觉得获取的数据可能会被修改, mybatis会利用序列化&反序列胡克隆一份新的数据给你 安全,速度慢 - size :存放多少元素
- type: 指定自定义缓存的全类名,实现Cache接口即可
- eviction 缓存的回收策略
二级缓存有关的设置
- 全局setting中设置
cacheEnabled=true
;false:关闭缓存(二级缓存关闭)(一级缓存一直可用) - 每一个select标签都有
useCache="true",
false,一级缓存可用,二级缓存关闭 - 每个增删改标签的:
flushCache="true"
,增删改执行完成后就会清空缓存(一级缓存清空二级缓存也会被清空) - 查询标签
flushCache="false"
,如果作为true,则每次查询之前都会清空缓存 - sqlSession.clearCache();清空当前session的一级缓存
- localCacheScope:本地缓存作用域
- 一级缓存SESSION:当前回话的所有数据保存在回话缓存中
- STATEMENT:可以禁用一级缓存
- 当在某一个作用域(一级缓存session/二级缓存Namespaces)进行了增/删/改操作后,默认该作用域中所有的select中的缓存将会被clear
尚硅谷B站Mybatis教程: Mybatis从入门到精通