MyBatis笔记(4)-动态SQL与缓存

动态SQL

在前面,我们学习了MyBatis的获取参数的功能,通过获取参数可以实现SQL的动态变化。在MyBatis中,还提供一种根据特定条件动态拼接SQL语句的功能,利用不同功能的标签,来动态拼接SQL语句。

if

if标签中我们可以指定test条件,这是一个条件表达式,如果表达式的结果为true,则会将标签中的内容进行拼接,反之则不会拼接。

1
2
3
4
5
6
7
8
9
10
<!--List<Emp> getEmpListByCondition(Emp emp);-->
<select id="getEmpListByMoreTJ" resultType="Emp">
select * from emp where 1=1
<if test="empId != '' and empId != null">
and emp_id = #{empId}
</if>
<if test="empName != '' and empName != null">
and emp_name = #{empName}
</if>
</select>

注意这里的test中,表达式之间的与和或的关系使用andor来表示

上面的SQL语句拼接,每个条件都有可能不成立,就导致都无法进行拼接。但是where后面一定需要根据

where

上面的示例中,我们使用where 1=1来避免全部条件不匹配时的SQL语句错误问题,不过在MyBatis中也提供了where标签来实现相应的功能。

1
2
3
4
5
6
7
8
9
10
11
12
<!--List<Emp> getEmpListByCondition(Emp emp);-->
<select id="getEmpListByMoreTJ" resultType="Emp">
select * from emp
<where>
<if test="empId != '' and empId != null">
emp_id = #{empId}
</if>
<if test="empName != '' and empName != null">
and emp_name = #{empName}
</if>
</where>
</select>

where标签一般和if标签结合使用,它可以达到以下的效果:

  1. 如果where标签中的if条件都不满足,则where标签不会添加where关键字
  2. 如果where标签中的if条件满足,则where标签会自动添加where关键字,并且会将条件最前方多余的and去除,但是不能去除条件后方多余的and

trim

前面的where可以用于动态添加where关键字以及去除条件最前面多余的关键字,而trim标签可以提供更多的功能,用于删除或添加内容。

trim标签的常用属性如下:

  • prefix:在trim标签包裹内容的前面添加某些内容
  • prefixOverrides:在trim标签包裹内容的前面删除某些内容
  • suffix:在trim标签包裹内容的后面添加某些内容
  • suffixOverrides:在trim标签包裹内容的后面删除某些内容
1
2
3
4
5
6
7
8
9
10
11
12
<!--List<Emp> getEmpListByCondition(Emp emp);-->
<select id="getEmpListByMoreTJ" resultType="Emp">
select * from emp
<trim prifix="where" suffixOverrides="and">
<if test="empId != '' and empId != null">
emp_id = #{empId} and
</if>
<if test="empName != '' and empName != null">
emp_name = #{empName}
</if>
</trim>
</select>

上面的动态SQL语句,可以自动添加where关键字,并删除最后多余的and关键字。

choose,when,otherwise

choose、when、otherwise标签三者通常结合使用,表示在多选一的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--List<Emp> getEmpListByCondition(Emp emp);-->
<select id="getEmpListByMoreTJ" resultType="Emp">
select * from emp
<where>
<choose>
<when test="empId != '' and empId != null">
emp_id = #{empId}
</when>
<when test="empName != '' and empName != null">
emp_name = #{empName}
</when>
<otherwise>
1 = 1
</otherwise>
</choose>
</where>
</select>

foreach

foreach可以用来进行集合的展开,在批量操作中非常好用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!--int insertEmps(List<Emp> emps);-->
<insert id="insertEmps">
insert into emp values
<foreach collection="emps" item="emp" separator=",">
(null,#{emp.empName},#{emp.deptId})
</foreach>
</insert>

<!--int deleteEmpByArray(int[] eids);-->
<delete id="deleteEmpByArray">
delete from emp where
<foreach collection="eids" item="eid" separator="or">
eid = #{eid}
</foreach>
</delete>

<!--int deleteEmpByArray(int[] eids);-->
<delete id="deleteEmpByArray">
delete from emp
where emp_id in
<foreach collection="eids" item="eid" separator="," open="(" close=")">
#{eid}
</foreach>
</delete>

这里的分隔符前后会自动添加空格。

sql片段

SQL片段对应sql标签,它用来记录一段公共的SQL片段,在需要使用的地方,通过include标签进行引入即可。

1
2
3
4
5
<sql id="empColumns">
emp_id, emp_name, dept_id
</sql>

select <include refid="empColumns"/> from emp

缓存

MyBatis中存在缓存机制,分为一级和二级缓存。同时MyBatis中也提供了整合第三方缓存的功能。

一级缓存

一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存。下次查询相同的数据,则会直接命中缓存,不需要从数据库重新访问。不过会有一些情况使得一级缓存失效:

  1. 不同的SqlSession对应不同的一级缓存
  2. 使用了同一个SqlSession,但是查询条件不同,无法命中缓存
  3. 同一个SqlSession的两次查询期间执行了增删改操作,导致缓存与数据库不一致,缓存失效
  4. 同一个SqlSession的两次查询期间手动清空了缓存,调用了SqlSession的clearCache()方法

二级缓存

二级缓存是SqlSessionFactory级别的,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存。

二级缓存开启的条件如下:

  1. 在核心配置文件中,设置全局配置属性cacheEnabled为true,当然它的默认值就是true
  2. 在映射文件中设置标签<cache/>
  3. 二级缓存在SqlSession关闭或提交后有效,一级缓存对应的SqlSession关闭或提交后,才会存入二级缓存中
  4. 查询的数据所对应的实体类类型必须要实现序列化接口(与后面的只读属性有关)

如果两次查询之间执行了任意的增删改操作,会使得一级和二级缓存同时失效。在查询的时候,优先查询二级缓存,然后再查询一级缓存,最后查询数据库。

在映射文件的<cache/>标签中可以设置二级缓存的相关属性:

  1. eviction:缓存回收策略,可选值如下,默认值为LRU
    • LRU:最近最少使用策略
    • FIFO:先进先出策略
    • SOFT:软引用
    • WEAK:弱引用
  2. flushInterval:表示缓存刷新间隔,单位为ms。默认情况没有设置,表示没有刷新间隔,在调用语句的时候刷新
  3. size:表示引用数目,为正整数。代表缓存最多可以存储对象的个数,如果设置的过大容易导致内存溢出
  4. readOnly:表示是否只读,可选值为true|false,默认值为false
    • true:只读缓存。在缓存命中的时候会给所有调用者返回缓存对象的相同示例,因此这些对象不能被修改。这种情况下性能更优。
    • false:读写缓存。在缓存命中的时候返回缓存对象的拷贝,因此需要对象实现序列化。这种情况下的性能差一些,但是更加安全。

MyBatis笔记(4)-动态SQL与缓存
http://example.com/2022/10/11/MyBatis笔记-4-动态SQL与缓存/
作者
EverNorif
发布于
2022年10月11日
许可协议