九、MyBatis动态SQL
9、1什么是动态SQL
动态 SQL 是 MyBatis 的强大特性之一。在 开发过程中,经常出现开发人员需要手动拼接 SQL 语句。根据不同的条件拼接 SQL 语句是一件极其痛苦的工作。例如,拼接时要确保添加了必要的空格,还要注意去掉列表最后一个列名的逗号。而动态 SQL 恰好解决了这一问题,可以根据场景动态的构建查询。
动态 SQL 大大减少了编写代码的工作量,更体现了 MyBatis 的灵活性、高度可配置性和可维护性。
9、2if
if结构,类似Java中的判断结构,根据参数的情况,可以动态拼接SQL语句
<select id="findAll" resultMap="bookType" useCache="true">select b.id,b.name,b.price,l.lid,l.userId,l.bookId from book b left join log l on b.id=l.bookId<!--在SQL语句执行时,根据name的情况进行判定,满足时,会根据name进行模糊查询,不满足时,则执行查询所有数据--><if test="name!=null">where name like #{name}</if>
</select>
if结构也可以实现多个参数拼接,依据参数的不同,对SQL语句进行拼接
<select id="findAll" resultMap="bookType" useCache="true">select b.id,b.name,b.price,l.lid,l.userId,l.bookId from book b left join log l on b.id=l.bookId where 1=1<if test="name!=null">and name like #{name}</if><if test="price!=0">and price > #{price}</if>
</select>
9、3choose (when, otherwise)
在MyBatis中,只有if结构,没有if—else结构,为了满足这种需求,MyBatis设计了choose—when—otherwise结构,这个结构类似于Java中的switch—case—default结构。
<select id="findAll" resultMap="bookType" useCache="true">select b.id,b.name,b.price,l.lid,l.userId,l.bookId from book b left join log l on b.id=l.bookId where 1=1<!--动态SQL传参时,当满足条件时,只执行最先满足条件的when,都不满足时,执行otherwise中的拼接SQL语句--><choose><when test="name!=null and name!=''">and name like #{name}</when><when test="price>0">and price>#{price}</when><otherwise>order by price desc</otherwise></choose>
</select>
9、3where
在动态SQL中,因为参数的情况不同,需要灵活的组织条件,但是不论多少条件,都需要使用where条件进行筛选,但是有的时候我们在条件都不满足时,就没有必要加入where关键字
<select id="findAll" resultMap="bookType" useCache="true">select b.id,b.name,b.price,l.lid,l.userId,l.bookId from book b left join log l on b.id=l.bookId <where><if test="name!=null and num!=''">and name like #{name}</if><if test="price>0">and price > #{price}</if></where>
</select>
当if的条件满足时,动态SQL会自动匹配where关键字
9、4set
依照where标签的特性,MyBatis根据这种情况,又提供了set标签,在执行update语句时,可以配合set标签,依据if条件动态拼接SQL
<update id="updateBook" parameterType="com.demo.entity.Book">update book <set><if test="name!=null and name!=''">name=#{name}</if><if test="price>=0">price=#{price}</if></set>where id=#{id}
</update>
9、5trim
在MyBatis动态SQL中,还有比where和set更灵活的动态拼接方式,就是trim标签
<select id="findAll" resultMap="bookType" useCache="true">select b.id,b.name,b.price,l.lid,l.userId,l.bookId from book b left join log l on b.id=l.bookId <trim prefix="where" prefixOverrides="and"><if test="name!=null and num!=''">and name like #{name}</if><if test="price>0">and price > #{price}</if></trim>
</select>
trim标签共有四个属性
- prefix:给SQL语句拼接的前缀,为 trim 包含的内容加上前缀
- prefixOverrides:去除 SQL 语句前面的关键字或字符,该关键字或者字符由 prefixOverrides 属性指定
- suffix:给SQL语句拼接的后缀,为 trim 包含的内容加上后缀
- suffixOverrides:去除 SQL 语句后面的关键字或者字符,该关键字或者字符由 suffixOverrides 属性指定。
9、6foreach
在动态SQL中经常遇见需要多个值或多个参数进行组合操作,MyBatis在提供了类似Java中的循环操作,通过循环,实现动态SQL
<insert id="insertBook2" parameterType="list" keyProperty="id" useGeneratedKeys="true" >insert into book(name,price) values<!--使用动态SQL中的循环标签foreach,collection要循环的集合,item集合中存储内容的临时变量,separator值之间的分隔符号,open表示以什么符号开头,close表示以什么符号结束--><foreach collection="list" item="book" separator="," open="(" close=")">#{book.name},#{book.price}</foreach>
</insert>
使用 foreach 标签时,最关键、最容易出错的是 collection 属性,该属性是必选的,但在不同情况下该属性的值是不一样的,主要有以下 3 种情况:
- 如果传入的是单参数且参数类型是一个 List,collection 属性值为 list。
- 如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array。
- 如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key。
9、7bind
在MyBatis中,使用bind标签对内容进行特殊处理,将参数设置成为需要特殊处理的值
<select id="findAll" resultMap="bookType" useCache="true"><bind name="bookName" value="'%'+name+'%'"/>select b.id,b.name,b.price,l.lid,l.userId,l.bookId from book b left join log l on b.id=l.bookId <where><if test="name!=null and num!=''">and name like #{bookName}</if><if test="price>0">and price > #{price}</if></where>
</select>