1.if
动态 SQL 通常要做的事情是有条件地包含 where 子句的一部分。比如:
2
3
4
5
6
7
8
9
|
<select id=
"findActiveBlogWithTitleLike"
resultType=
"Blog"
>
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<
if
test=
"title != null"
>
AND title like #{title}
</
if
>
</select>
|
这条语句提供了一个可选的文本查找类型的功能。如果没有传入“title”,那么所有处于“ACTIVE”状态的BLOG都会返回;反之若传入了“title”,那么就会把模糊查找“title”内容的BLOG结果返回(就这个例子而言,细心的读者会发现其中的参数值是可以包含一些掩码或通配符的)。
如果想可选地通过“title”和“author”两个条件搜索该怎么办呢?首先,改变语句的名称让它更具实际意义;然后只要加入另一个条件即可。
2
3
4
5
6
7
8
9
10
11
|
<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>
|
2. choose, when, otherwise
有些时候,我们不想用到所有的条件语句,而只想从中择其一二。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
还是上面的例子,但是这次变为提供了“title”就按“title”查找,提供了“author”就按“author”查找,若两者都没有提供,就返回所有符合条件的BLOG(实际情况可能是由管理员按一定策略选出BLOG列表,而不是返回大量无意义的随机结果)。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<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>
|
3. trim, where
前面几个例子已经合宜地解决了一个臭名昭著的动态 SQL 问题。现在考虑回到“if”示例,这次我们将“ACTIVE = 1”也设置成动态的条件,看看会发生什么。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<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’
这个查询也会失败。这个问题不能简单的用条件句式来解决,如果你也曾经被迫这样写过,那么你很可能从此以后都不想再这样去写了。
MyBatis 有一个简单的处理,这在90%的情况下都会有用。而在不能使用的地方,你可以自定义处理方式来令其正常工作。一处简单的修改就能得到想要的效果:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<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 属性中指定的内容。
4. trim set
类似的用于动态更新语句的解决方案叫做 set。set 元素可以被用于动态包含需要更新的列,而舍去其他的。比如:
2
3
4
5
6
7
8
9
10
11
|
<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 关键字,同时也会消除无关的逗号,因为用了条件语句之后很可能就会在生成的赋值语句的后面留下这些逗号。
若你对等价的自定义 trim 元素的样子感兴趣,那这就应该是它的真面目:
2
3
4
|
<trim prefix=
"SET"
suffixOverrides=
","
>
...
</trim>
|
注意这里我们忽略的是后缀中的值,而又一次附加了前缀中的值。