MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦。拼接的时候要确保不能忘了必要的空格,还要注意省掉列名列表最后的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦,通常使用动态 SQL 不可能是独立的一部分,MyBatis
当然使用一种强大的动态 SQL 语言来改进这种情形,这种语言可以被用在任意的 SQL 映射语句中。
动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多的元素需要来了解。MyBatis 3 大大提升了它们,现在用不到原先一半的元素就可以了。MyBatis 采用功能强大的基于 OGNL 的表达式来消除其他元素。
* if
* foreach
* choose (when, otherwise)
* trim (where, set)
1.if
<!--
动态 SQL.
1. 若查询条件中有 > 或 <, 则此部分代码需要放在 CDATA 中.
2. 使用 where 和 if 节点来构建动态 SQL.
1). 若 where 中的 if 一个都不满足, 则就不会添加 where 子句.
2). 若 where 中有一个 if 满足条件, 则就会自动的生成 where 子句,且会根据实际情况,删除 if 中的 AND 或 OR.
-->
<select id="testIf" resultType="com.atguigu.mybits.beans.Order">
SELECT order_Id,order_Name
FROM orders
<where>
<if test="minId != null">
<![CDATA[
order_Id>= #{minId}
]]>
</if>
<if test="maxId != null">
<![CDATA[
AND order_Id<= #{maxId}
]]>
</if>
</where>
</select>
List<Order> testIf(Map<String, Object> params);
@Test
public void testIf(){
Map<String, Object> params = new HashMap<String, Object>();
params.put( "minId", 2);
params.put( "maxId", 12);
List<Order> orders = orderMapper.testIf(params);
System. out.println(orders.size());
}
2. foreach
动态 SQL 的另外一个常用的必要操作是需要对一个集合进行遍历,通常是在构建 IN 条件语句的时候。比如:
<!--
动态 SQL: 使用 foreach 可以完成 IN 操作符的效果.
若传入的 ids 为 {1,2,3,4} 生成后的效果如下:
(1,2,3,4)
-->
<select id="testForEach" resultType="com.atguigu.mybits.beans.Order">
SELECT order_Id, order_Name
FROM orders
WHERE order_Id IN
<foreach collection="ids" item="orderId" open="(" close=")" separator=",">
#{orderId}
</foreach>
</select>
List<Order> testForEach(@Param("ids") List<Integer> ids);
@Test
public void testForEach(){
List<Integer> ids = Arrays.asList(1,2,4,5);
List<Order> orders = orderMapper.testForEach(ids);
System.out.println(orders.size());
}
foreach 元素的功能是非常强大的,它允许你指定一个集合,声明可以用在元素体内的集合项和索引变量。它也允许你指定开闭匹配的字符串以及在迭代中间放置分隔符。这个元素是很智能的,因此它不会偶然地附加多余的分隔符。
你可以将任何可迭代对象(如列表、集合等)和任何的字典或者数组对象传递给foreach作为集合参数。当使用可迭代对象或者数组时,index是当前迭代的次数,item的值是本次迭代获取的元素。当使用字典(或者Map.Entry对象的集合)时,index是键,item是值。
3.choose, when, otherwise
有些时候,我们不想用到所有的条件语句,而只想从中择其一二。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
<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>
4.trim, where, set
前面几个例子已经合宜地解决了一个臭名昭著的动态 SQL 问题。现在考虑回到“if”示例,这次我们将“ACTIVE = 1”也设置成动态的条件,看看会发生什么。<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>
<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 WHER
这会导致查询失败。如果仅仅第二个条件匹配又会怎样?这条 SQL 最终会是这样:
SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’
这个查询也会失败。这个问题不能简单的用条件句式来解决,如果你也曾经被迫这样写过,那么你很可能从此以后都不想再这样去写了。
MyBatis 有一个简单的处理,这在90%的情况下都会有用。而在不能使用的地方,你可以自定义处理方式来令其正常工作。一处简单的修改就能得到想要的效果:
<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>
where 元素知道只有在一个以上的if条件有值的情况下才去插入“WHERE”子句。而且,若最后的内容是“AND”或“OR”开头的,where 元素也知道如何将他们去除。
如果 where 元素没有按正常套路出牌,我们还是可以通过自定义 trim 元素来定制我们想要的功能。比如,和 where 元素等价的自定义 trim 元素为:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
prefixOverrides 属性会忽略通过管道分隔的文本序列(注意此例中的空格也是必要的)。它带来的结果就是所有在 prefixOverrides 属性中指定的内容将被移除,并且插入
prefix 属性中指定的内容。
类似的用于动态更新语句的解决方案叫做 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>
这里,set 元素会动态前置 SET 关键字,同时也会消除无关的逗号,因为用了条件语句之后很可能就会在生成的赋值语句的后面留下这些逗号。