Mybatis动态SQL
概念
摘自官网:https://mybatis.org/mybatis-3/zh/dynamic-sql.html
MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
虽然在以前使用动态 SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。
动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。
动态sql语句标签主要包括下面这几种:
- if
- choose…when…otherwise
- trim
- foreach
- bind
if 标签
当我们需要做一些判断的时候,可以使用到if标签,如下:
1 | <select id="findUsersByUsernamePassword" resultType="user" > |
所对应的Mapper接口方法:
1 | /*根据username和password查询*/ |
我们可以直接使用对象中的属性来取值。如果此时我们加了@Param("user")
1 | /*根据username和password查询*/ |
对应的取值xml:
1 | <select id="findUsersByUsernamePassword" resultType="user" > |
解释:上述示例中,sql拼接结果如下:
username 为null,password 不为null
1
select * from tb_user where password = ?
username 不为null,password为null
1 | select * from tb_user where username = ? |
username 不为null,password不为null
1
select * from tb_user where username = ? and password = ?
username 为null,password为null
1 | select * from tb_user |
注意:
1 | <if test="id != null and userId != '' and username =='2'"> |
mybatis是用OGNL表达式来解析的,在OGNL的表达式中,’2’会被解析成字符,java是强类型的,char 和一个string 会导致不等,所以if标签中的sql不会被解析。
- if标签里,判断相等或不相等
1 | 单个字符需要加.toString(),如:auditidentified =='2'.toString() |
- if标签里,判断相等或不相等
1 | 多个字符不需要加toString(),如:auditidentified !='2155' |
choose 标签
有时我们不希望用到所有的条件,比如用户有记录有id,user_id, username这几个属性,但是查询的时候希望有优先级,比如id存在的时候,只按id查询,如果id不存在,但是user_id存在,则按照user_id查询,如果前面两者都不存在,则按照username查询。
语句模板:
1 | <choose> |
语句解释:
1 | 解释:(只会执行一个条件,也必定会有一个条件执行) |
示例语句如下:
1 | <select id="findUsersByIdOrUserIdOrUsername" resultType="user"> |
对应的Mapper接口方法:
1 | /*查询,如果有id不为空,则直接按id查询,如果id为空,则按userId查询,否则按username查询*/ |
注意的同学可以发现,这里与之前的传参方法不同。后面将会有一篇专门介绍参数传递问题。这里先不细说。
trim 标签
与trim相关的标签还有set和where标签,这是用来我们解决sql拼接的问题,之前自己手动凭借SQL的时候,需要非常注意 and,or和逗号问题,对应and|or,我们会在sql之前使用where 1 = 1,如下:
1 | select * from tb_user where 1 = 1 and ... |
我们看一下下面这个sql:
1 | <select id="findUsersByUsernamePassword" resultType="user" > |
当我username为空,password不为空时,处理之后就会生成如下sql:
1 | select * from tb_user where and password = ? |
上面这个sql就是一个错误的sql,但是mybatis中的动态sql帮我们提供了方法,下面我们看一下trim标签:
1 | <trim prefix="" prefixOverrides="" suffix="" suffixOverrides=""></trim> |
所以我们使用trim标签来解决上面标签产生的问题,如下:
1 | <select id="findUsersByUsernamePassword" resultType="user" > |
注意:and后面的空格是必要添加的(官网介绍)。但是我测试的空格没有影响,为了规避问题,建议加上空格。
mybatis鉴于上述使用环境非常常见,将其封装为where标签,以及set标签,如下:
1 | <select id="findUsersByUsernamePassword" resultType="user" > |
foreach标签
这个标签对我们来说从名字上,其实就知道是用来干啥的了。对,foreach标签就是用来循环迭代一些可迭代的元素,如,list,set,map,数组等。
试想以下,当我们需要根据多个id批量查询的时候,我们使用java代码需要如何操作:
1 | public void test() { |
可以看出,拼接这个sql非常繁琐,但是Mybatis提供的foreach可以很简单的实现,在此之前,我们看一下foreach标签:
1 | <foreach collection="" index="" item="" open="" separator="" close="" ></foreach> |
注意:
你可以将任何可迭代对象 如 List、Map 对象或者数组对象传递给 foreach 作为集合参数。当使用可迭代对象或者数组时。
- collection 属性的书写方式,List -> list/collection,Set->collection,[]数组 -> array,Map -> map.
index 是当前迭代的次数,item 的值是本次迭代获取的元素。
当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
- open表示拼接最前的元素
- close表示拼接在最后的元素
- separator表示遍历的各个元素之间的分隔符。
使用foreach实现:
1 | <select id="findUsersByIds" resultType="user"> |
对应的mapper接口的方法:
1 | List<User> findUsersByIds(List<Integer> id); |
bind 标签
bind 元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文:
1 | <select id="findUserById" resultType="user"> |
bind 标签比较简单,如上所示,可以将字符串进行拼接,也可以对传递进来的参数进行处理。
源码地址:
https://gitee.com/ooyhao/JavaRepo_Public/tree/master/Mybatis