动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
如果之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识,因为大致写法是差不多的。
if语句是最简单的,通常用在where处添加条件拼接查询条件。
mapper.xml
<select id="getStudentsIf"
resultType="Student" parameterType="map">
select id,name
from student
where 1=1
<if test="deptId != null">
and dept_id = #{deptId}
if>
select>
测试代码(下面的例子中测试都是靠map这种传参)
Map map = new HashMap<>();
map.put("deptId",3);
List<Student> list = studentMapper.getStudentsIf(map);
list.forEach(System.out::println);
通过这个例子可以看到,通过if语句判断参数中deptId是否为空,根据判定结果追加SQL。如果这是deptId是3则查询班级ID是3是学生,null则查询全部学生。
问 :那里的where 1=1是啥?
答:为了保证sql可以执行。
问:怎么优化?
答:这里介绍一下
标签,我们知道在拼接条件时,从第二个条件开始要加OR或者AND,但若只有一个条件就不用加AND,但是如何控制呢?
标签帮助我们智能识别了,
中有条件成立,他就会自动加上在sql上拼接上“where”,如果第一个成立条件要拼接的sql是and id = #{id}
,就会自动帮助我们省忽略and,但若
中无成立的条件,mybatis咋会执行
之前的语句。
<select id="getStudentsIf"
resultType="Student" parameterType="map">
select id,name
from student
<where>
<if test="deptId != null">
and dept_id = #{deptId}
if>
where>
select>
可见,利用where标签才是高级做法。
如果我们不想要全部的条件,只想取其中一个,而上面的if是无数个单独的if语句,没有else if 和else,针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch-case语句。
标签相当于最外层的switch,而
相当于每一个case。
是默认的。
mapper.xml
<select id="getStudentsChoose" resultType="Student" parameterType="map">
select id,name,dept_id
from student
<where>
<choose>
<when test="name != null">
name = #{name}
when>
<when test="deptId != null">
and dept_id = #{deptId}
when>
<otherwise>
and id = #{id}
otherwise>
choose>
where>
在这里我们传入的map中只有{"deptId":2}
,所以mybatis智能拼接上了dept_id = #{deptId}
,而其他对立条件不生效。
测试结果如下:
但若传入一个空的map,可见mybatis拼接的是
中的SQL。
测试结果如下:
提醒: 如果每个case都符合,那么只会拼接第一个
的SQL!
set一般用于update语句,和前面的where相似。
会帮助我们省略掉多余的“逗号,”
<update id="update" parameterType="map">
update Student
<set>
<if test="name != null">
name = #{name},
if>
<if test="deptId != null">
dept_id = #{deptId}
if>
set>
where id = #{id}
update>
在测试用例中我们传入一个{"id":2}
和{"name":二弟}
,可见mybatis帮我们拼接的SQL如下↓
可以发现
和
可以帮助我们忽略不必要的字符。但我们仍然可以通过自定义 trim 元素来定制 where /set元素的功能,例如
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
trim>
<trim prefix="SET" suffixOverrides=",">
...
trim>
标签中参数意义不多赘述,自行翻译理解。
假如我们要拼接不定个数的SQL,参数传入一个List,如何编写动态sql呢?
答:利用
<select id="getStudentsById" parameterType="map"
resultType="Student">
select id,name,dept_id
from student
<where>
<foreach collection="Ids" separator="or" item="id"
open="(" close=")">
id = #{id}
foreach>
where>
select>
foreach 标签中,collection对应传入要遍历集合的名字,separator是拼接的每个sql间的分隔符,item是遍历到的当前元素,open和close分别代表遍历前和后加的前后缀。
以上sql传入一个{"Ids",new int[]{1,2,3}}
,可见执行结果如下:
这样就把参数list中所有id对应学生查询出来啦。
也叫本地会话缓存。
针对于一个sqlsession,若想多次执行同一条sql,第一次会把查询结果保存在一级缓存中,再次查询时会直接去一级缓存中获取,不会再次访问数据库,大大减少了数据库的负压。
但当此sqlsession关闭连接后,一级缓存会清空。
也叫全局缓存。当sqlsession关闭连接后,会将一级缓存中的数据转存到二级缓存中。
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
<cache/>
并且在配置文件中开启缓存即可。
当然我们可以通过 cache 元素的属性来修改默认参数。比如:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
如果开启了二级缓存,映射语句文件中的所有 select 语句的结果将会被缓存,映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
当执行一条语句时的执行顺序:
1.先去二级缓存中查
2.再去一级缓存中查
3.到数据库中查。
至此,mybatis的相关内容已经结束啦!是不是很简单呢~后序我们还会学习mybatis-plus,敬请期待!