动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
if
select * from user where 1=1 <if test="id != null and id != ''"> and id = #{id} </if> <if test="name != null and name != ''"> and name = #{name} </if>
为什么使用1=1作为参数都很清楚了,如果没有1=1且条件if为true,那么拼接的sql语句会变成,是错误的拼接
如果没有匹配又没有1=1的条件会怎么样?
select * from user where
如果匹配的只是第二个条件又会怎样?
select * from user where and name = nameValue
if的多条件使用,即使没有1=1也不会出现问题,因为此时有一个固定条件,只要确保不出现 where 和 and的连接的情况即可。
<select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG WHERE state = ‘ACTIVE’ <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>
choose、when、otherwise
<select id="testStudayChoose" resultType="User"> select * from `user` <where> <choose> <when test="name != null and name != ''"> and name = #{name} </when> <when test="password != null and password != ''"> and password = #{password} </when> <otherwise> and uid = 1 </otherwise> </choose> </where>
</select>
下面会讲到<where>,其实就是代替了where 1=1。
值得注意的是choose标签相当于switch,满足第一个条件以后就不会执行其他语句拼接,例如name和password两个参数都传也只会执行name。
而otherwise标签则是相当于default当条件都不满足时执行的拼接sql。
trim、where、set
<where> </where>
前面几个例子已经合宜地解决了一个臭名昭著的动态 SQL 问题。现在回到之前的 “if” 示例,这次我们将 where 1=1 设置成动态条件
<select id="findOneSql" resultType="User"> select * from `user` <where> <if test="id != null and id != ''"> and uid = #{uid} </if> <if test="birthday != null"> and birthday = #{birthday} </if> </where> </select>
where标签在if条件都不满足时取消的where的条件拼接,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
trim标签也可以完成相同的功能
<select id="findOneSql" resultType="User"> select * from `user` <trim prefix="WHERE" prefixOverrides="AND |OR"> <if test="id != null and id != ''"> and uid = #{uid} </if> <if test="birthday != null"> and birthday = #{birthday} </if> </trim> </select>
值得注意的是trim标签中的每个属性
属性 | 描述 |
---|---|
prefix | 给sql语句拼接的前缀 |
suffix | 给sql语句拼接的后缀 |
prefixOverrides | 去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定,假设该属性指定为"AND",当sql语句的开头为"AND",trim标签将会去除该"AND" |
suffixOverrides | 去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定 |
<set></set>
<update id="updateOneSql"> update `user` <set> <if test="name != null and name != ''"> `name` = #{name}, </if> <if test="birthday != null"> birthday = #{birthday} </if> </set> where uid = #{uid} </update>
<-- 或使用的写法 -->
<trim prefix="SET" suffixOverrides=","> ... </trim>
foreach
例如使用场景为 uid in () 括号中位多个条件
<select id="setValueListSql" parameterType="java.util.List" resultType="User"> SELECT * from `user` where uid in <foreach collection="list" open="(" separator="," close=")" item="item" index="index"> #{item} </foreach> </select>
- collection:表示传入过来的参数的数据类型。该参数为必选。要做 foreach 的对象,作为入参时,List 对象默认用 list 代替作为键,数组对象有 array 代替作为键,Map 对象没有默认的键。当然在作为入参时可以使用 @Param(“keyName”) 来设置键,设置 keyName 后,list,array 将会失效。 除了入参这种情况外,还有一种作为参数对象的某个字段的时候。举个例子:
如果 User 有属性 List ids。入参是 User 对象,那么这个 collection = “ids” 如果 User 有属性 Ids ids;其中 Ids 是个对象,Ids 有个属性 List id;入参是 User 对象,那么 collection = “ids.id”
- 如果传入的是单参数且参数类型是一个 List 的时候,collection 属性值为 list
- 如果传入的是单参数且参数类型是一个 array 数组的时候,collection 的属性值为 array
- 如果传入的参数是多个的时候,我们就需要把它们封装成一个 Map 了,当然单参数也可以封装成 map。
- item: 循环体中的具体对象。支持属性的点路径访问,如 item.age,item.info.details。具体说明:在 list 和数组中是其中的对象,在 map 中是 value,该参数为必选。(它是每一个元素进行迭代时的别名)
- index:在 list 和数组中,index 是元素的序号;在 map 中,index 是元素的 key。
- open:表示该语句以什么开始
- close:表示该语句以什么结束
- separator:表示在每次进行迭代之间以什么符号作为分隔符
补充
在insert时使用foreach批量插入
insert into user(id,name) values <foreach collection="list" separator="," item="item" index="index"> (#{item.id},#{item.name}) </foreach>