Mybatis【13】-- Mybatis动态Sql标签的使用

第十六封 2021-01-21 00:35:07
mybatis SQL 标签 动态


mybatis有一个强大的特性,其他框架在拼接sql的时候要特别谨慎,比如哪里需要空格,还要注意去掉列表最后一个列名的逗号,mybtis的动态sql可以帮助我们逃离这样的痛苦挣扎,那就是动态SQL.它还可以处理一种情况,当你不确定你的参数不知道是不是为空的时候,我们不需要在业务逻辑中判断,直接在sql中处理,代码无比简洁。主要的动态sql标签如下:

  • <if></if>
  • <where></where>(trim,set)
  • <choose></choose>(when, otherwise)
  • <foreach></foreach>

注意事项:
在mapper中如果出现大于号(>),小于号(),大于等于号(),小于等于号()等,最好需要转换成为实体符号,这是因为mapper是XML文件,xml文件本身就含有较多的<>这样的尖括号,所以解析的时候可能会解析出错。

原符号 < <= > >= & ' "
替换符号 &lt; &lt;= &gt; &gt;= &amp; &apos; &quot;

<if>

我们经常需要根据where后面的条件筛选出需要的数据,当多个条件拼接的时候,我们一般使用<if></if>,如果if里面的条件成立,那么就会使用标签的语句,但是我们可以知道where句子第一个标签是没有and的,而后面的条件都需要and,所以有一种做法是第一个使用where 1 = 1,这个条件恒成立,后面的所有子语句都加上and,如果增加判断,那么我们只需要加<if>标签就可以了。

 <!-- 动态sql if标签-->
<!-- &可以使用and来代替 ,注意!=需要连在一起写-->
<select id="selectStudentByDynamicSQL" resultType="Student">
<!--最常用的(动态参数) select id,name,age,score from student where name like '%' #{name} '%' -->
<!-- 下面的是字符串拼接 ,只能写value,了解即可,容易sql注入,执行效率低,不建议使用-->
select id,name,age,score
from student
where 1=1
<if test="name != null and name != ''">
and name like '%' #{name} '%'
</if>
<if test="age > 0">
and age > #{age}
</if>
</select>

当有两个查询条件的时候,sql语句是:select * from student where 1=1 and name like '%' ? '%' and age > ?

当有一个查询条件的时候:sql语句就变成:select * from student where 1=1 and name like '%' ? '%'

当没有查询条件的时候,sql语句是:
select * from student where 1=1

<if></if>标签需要手动在where后面添加1=1语句,这是因为如果<if>后面的条件都是false的时候,where后面如果没有1=1语句,sql就剩下一个空空的where,sql就会报错。所以在where后面需要加上永真句子1=1,但是这样有一个问题,当数据量比较大的时候,会严重影响sql的查询效率。

<where></where>,<trim></trim>,<set></set>

使用<where></where>标签,在有查询语句的时候,自动补上where子句,在没有查询条件的时候,不会加上where子句,这也就解决了我们上面所涉及到的问题,剩下的就是<if>标签的and子句,第一个,<if>片段里面可以不包含and,也可以包含,系统会自动去掉and,但是其他的<if>片段里面的and,必须写上,否则会出错。下面的写法中,如果name为null,第二个if标签中的if也会被去掉,不会报错。

 <select id="selectStudentByDynamicSQLWhere" resultType="Student">
<!--最常用的(动态参数) select id,name,age,score from student where name like '%' #{name} '%' -->
<!-- 下面的是字符串拼接 ,只能写value,了解即可,容易sql注入,执行效率低,不建议使用-->
select id,name,age,score
from student
<where>
<if test="name != null and name != ''">
and name like '%' #{name} '%'
</if>
<if test="age > 0">
and age > #{age}
</if>
</where>
</select>

如果where里面是不规范的,那我们可以通过 来自定义where元素的功能, 标签主要有以下属性:

  • prefix:在包含的内容前加上前缀,不是百分之百会加,会根据需要自动加
  • suffix:在包含的内容后面加上后缀,不是百分之百会加,会根据需要自动加
  • prefixOverrides:可以把包含内容的首部某些内容忽略(不能自己增加),不一定会忽略,根据需要自动忽略
  • suffixOverrides:也可以把包含内容的尾部的某些内容忽略(不能自己增加),同上

下面这样的是错误的,当传入的name不为空,而且age大于0的时候

 <select id="selectStudentByDynamicSQLWhere" resultType="Student">
select id,name,age,score
from student
<trim prefix="where" prefixOverrides="and">
<if test="name != null and name != ''">
name like '%' #{name} '%'
</if>
<if test="age > 0">
age > #{age}
</if>
</trim>
</select>

不会自己增加and在第二个age前面:



下面的是正确的,我们在两个<if>标签前面都增加了and,第二个and会自动去掉:

 <select id="selectStudentByDynamicSQLWhere" resultType="Student">
select id,name,age,score
from student
<trim prefix="where" prefixOverrides="and">
<if test="name != null and name != ''">
and name like '%' #{name} '%'
</if>
<if test="age > 0">
and age > #{age}
</if>
</trim>
</select>

下面是后缀模式, prefix="set"表示在整个语句前面加上前缀set, suffixoverride=","表示每一个语句后面的后缀","可以被忽略,如果是需要的话suffix=" where id = #{id}表示在整个语句后面增加where id = #{id},:

update user
<trim prefix="set" suffixoverride="," suffix=" where id = #{id} ">
<if test="name != null and name.length()>0"> name=#{name} , </if>
<if test="age != null "> age=#{age} , </if>
</trim>

当然,我们对上面的语句还有动态解决的方案,那就是 标签:

 <update id="updateStudent">
update student
<set>
<!-- 第一个if标签的逗号一定要有,最后一个标签的逗号可以没有-->
<if test="name != null"> name=#{name},</if>
<if test="age != null">age=#{age},</if>
<if test="score != null"> score=#{score},</if>
</set>
where id=#{id}
</update>

<choose>, <when>, <otherwise>

有时候,我们只想去匹配第一个条件,或者第一个条件不匹配的时候才会去匹配第二个条件,不像<where></where>标签里面的<if></if>一样会去判断所有的子语句是否可以匹配,而是遇到一个匹配的就会执行跳出<choose></choose>

 <!-- selectStudentByDynamicSQLChoose 类似于switch,满足后就不会判断后面的了-->
<!-- 如果名字不为空,那么按照名字来查询,如果名字为空,就按照年龄来查询,如果没有查询条件,就没有查询条件 -->
<select id="selectStudentByDynamicSQLChoose" resultType="Student">
<!--最常用的(动态参数) select id,name,age,score from student where name like '%' #{name} '%' -->
select id,name,age,score
from student
<where>
<choose>
<when test="name != null and name != ''">
and name like '%' #{name} '%'
</when>
<when test="age > 0">
and age > #{age}
</when>
<otherwise>
and 1 != 1
</otherwise>
</choose>
</where>
</select>

<choose>标签就像是switch语句,每一个<when>都像是case,后面默认跟上break语句,只要满足一个就不会判断后面的子语句了,当前面所有的<when></when>都不执行的时候,就会执行<otherwise></otherwise>标签的内容,这个内容也就像是switch语句里面的default。

foreach

动态SQL要有一个比较多的操作是对一个集合进行遍历,通常是在构建IN条件语句的时候。需要注意的点:

  • collection 表示需要遍历的集合类型,array表示需要遍历的数组
  • open,close,separator是对遍历内容的SQL拼接
  • foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及在迭代结果之间放置分隔符。
  • 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象传递给 foreach 作为集合参数。当使用可迭代对象或者数组时,index 是当前迭代的次数,item 的值是本次迭代获取的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

1.比如我们需要查找学生的id为1,2,3的学生信息,我们不希望分开一次査一个,而是希望将数组id一次传进去,查出来一个学生的集合。

sql接口可以这样写,传入一个对象的数组:

public List<Student>selectStudentByDynamicSQLForeachArray(Object[]studentIds);

sql语句如下,遍历array数组的时候,指定左边符号是左括号,右边是右括号,元素以逗号分隔开:

 <!-- select * from student where id in (1,3) -->
<select id="selectStudentByDynamicSQLForeachArray" resultType="Student">
select id,name,age,score
from student
<if test="array !=null and array.length > 0 ">
where id in
<foreach collection="array" open="(" close=")" item="myid" separator=",">
#{myid}
</foreach>
</if>
</select>

2.当遍历的是一个类型为int的list列表时:

public List<Student>selectStudentByDynamicSQLForeachList(List<Integer>studentIds);

sql语句如下,colleaction指定为list:

 <select id="selectStudentByDynamicSQLForeachList" resultType="Student">
select id,name,age,score
from student
<if test="list !=null and list.size > 0 ">
where id in
<foreach collection="list" open="(" close=")" item="myid" separator=",">
#{myid}
</foreach>
</if>
</select>

3.当遍历的是一个类型为对象的list:

public List<Student>selectStudentByDynamicSQLForeachListStudent(List<Student>students);

sql语句里面与上面相似,只是在使用属性的时候不太一样:

<select id="selectStudentByDynamicSQLForeachListStudent" resultType="Student">
select id,name,age,score
from student
<if test="list !=null and list.size > 0 ">
where id in
<foreach collection="list" open="(" close=")" item="stu" separator=",">
#{stu.id}
</foreach>
</if>
</select>

<sql></sql>

用于定义sql片段,方便在其他SQL标签里面复用,在其他地方复用的时候需要使用<include></include>子标签,<sql>可以定义sql的任何部分,所以<include>标签可以放在动态SQL的任何位置。

 <sql id="selectHead">
select id,name,age,score
from student
</sql>
<!-- 可读性比较差 -->
<select id="selectStudentByDynamicSQLfragment" resultType="Student">
<include refid="selectHead"></include>
<if test="list !=null and list.size > 0 ">
where id in
<foreach collection="list" open="(" close=")" item="stu" separator=",">
#{stu.id}
</foreach>
</if>
</select>

动态sql让SQL写起来更加简洁,减少了很多重复代码,动态sql之间可以相互拼接,只要符合sql语句规范即可。

【作者简介】
秦怀,公众号【秦怀杂货店】作者,技术之路不在一时,山高水长,纵使缓慢,驰而不息。这个世界希望一切都很快,更快,但是我希望自己能走好每一步,写好每一篇文章,期待和你们一起交流。

版权声明
本文为[第十六封]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/Damaer/p/14305935.html

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云