动态 SQL
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
MyBatis的动态sql主要是通过MyBatis动态sql标签来实现的,主要的元素标签有以下几个
- if
- choose ,when,,otherwise
- trim ,where,,set
- foreach
下面就详细介绍下各个标签元素的使用
if标签
动态 SQL 通常要做的事情是有条件地包含 where 子句的一部分,所以在 MyBatis 中 元素是最常用的元素,它类似于 Java 中的 if 语句。
if标签是通过test子句的结果来判断是否拼接if标签内的sql语句,如果test语句为true则拼接,如果为false则不拼接
下面是一个简单例子,大家可以参考
<!--使用 if 元素根据条件动态查询用户信息-->
<select id="selectUserByIf" resultType="com.po.MyUser" parameterType="com.po.MyUser">
select * from user where 1=1
<if test="uname!=null and uname!=''">
and uname like concat('%',#{uname},'%')
</if >
<if test="usex !=null and usex !=''">
and usex=#{usex}
</if >
</select>
上面的例子,只有当uname和usex 参数不为null而且不为空字符串的时候才会拼接对应的sql语句
if标签的使用总结:
1、if标签的test属性必填,test属性为一个判断表达式,结果返回true或false
2、判断条件!= null 或 == null,适用于任何类型的字段,用于判断某个属性是否为空
3、test属性表达式有多个判断条件时,使用and或or进行连接,and相当于Java的与(&&)运算符,需要and连接的两个条件都为true才返回true,or相当于Java的或(||)运算符,两个条件中有一个为true,则返回true
choose标签
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
choose标签是通过choose标签内的标签来拼接sql语句,如果标签的判断表达式返回为true,则拼接该标签内的sql,并且会结束choose,这意味着choose只会选择一个符合条件的sql进行拼接,这就意味着哪怕一个choose标签内有多个符合条件的标签,它也只会选择第一个标签的sql语句。choose标签还有一个otherwise 标签,当 choose 中所有 when 的条件都不满则时,则拼接 otherwise 中的sql。
示例:
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
上面的例子,当传入了 “title” 就会按 “title” 查找,传入了 “author” ,就按 “author” 查找。若两者都没有传入,就添加featured 为1的条件
where标签
where 标签元素只会在子元素有返回内容的时候下才插入 “WHERE” 关键字。而且,如果第一个子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
这是为了解决以下问题
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:
SELECT * FROM BLOG
WHERE
这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样:
SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’
这个查询也会失败,这个问题不能简单地用条件元素来解决。但是我们用where标签就可以轻松解决上述问题
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
set标签
set 标签元素主要是用在更新操作的时候,它的主要功能和 where 标签元素其实是差不多的,主要是在包含的语句前输出一个 set,然后如果包含的语句是以逗号结束的话将会把该逗号忽略,如果 set 包含的内容为空的话则会出错。有了 set 元素就可以动态的更新那些修改了的字段。
例:
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
特别需要注意, 标签包含的内容不能为空,比如上面的例子,如果我们的if标签都为false,则内容为空,那么拼接的sql就是
update Author where id=#{id}
这样的sql语句明显是错误的
trim标签
trim标签是一个格式化的标签,主要用于拼接sql的条件语句(前缀或后缀的添加或忽略),它的灵活性较高,可以完成set或者是where标签的功能。以下是trim标签的四个主要的属性:
prefix:前缀内容,即会在trim语句前添加prefix指定的字符串
suffix:后缀内容,即会在trim语句后添加suffix指定的字符串
prefixOverrides:去除sql语句前面的某个关键字或者字符,比如当该属性指定为"AND",当trim拼接的sql语句的开头为"AND",trim标签将会去除该"AND"
suffixOverrides:去除sql语句前面的某个关键字或者字符,比如当该属性指定为","(逗号),当trim拼接的sql语句的结尾为","(逗号)时,trim标签将会去除该","(逗号)
比如,我们可以使用以下trim标签语句可以代替where标签
<trim prefix=”WHERE” prefixOverrides=“AND |OR ”>
...
</trim>
使用以下trim标签语句可以代替set标签
<trim prefix="SET" suffixOverrides=",">
...
</trim>
foreach标签
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。
foreach元素的属性主要有item,index,collection,open,separator,close。
item:当前迭代到的元素的别名,该参数为必选。
index:在list和数组中,index是元素的序号,在map中,index是元素的key,该参数可选
open:foreach代码的开始符号,一般是”(“,和close=")"合用。常用在in(),values()时。该参数可选
separator:元素之间的分隔符,例如在in()的时候,separator=","会自动在元素中间用“,“隔开,避免手动输入逗号导致sql错误,如in(1,2,)这样。该参数可选。
close: foreach代码的关闭符号,一般是)和open="("合用。常用在in(),values()时。该参数可选。
collection: 要做foreach遍历的对象,如果遍历对象为List对象,collection 属性值为"list",
如果遍历对象为 array 数组,collection 属性值为"array",
Map对象没有默认的键,当然在作为入参时可以使用@Param("keyName")来设置键,设置keyName后,list,array将会失效,使用map中的键名作为collection 的属性值。
除了入参这种情况外,还有一种作为参数对象的某个字段的时候。举个例子:如果User有属性List ids。入参是User对象,那么这个时候collection 就为collection = "ids".如果User有属性Ids ids;其中Ids是个对象,Ids有个属性List id;入参是User对象,那么collection = "ids.id"
在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:
1、如果传入的是单参数且参数类型是一个List的时候,collection属性值为list .
2、如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array .
3、如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Map的,map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key.
示例:
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>