• trim, where, set

    trim, where, set

    前面几个例子已经合宜地解决了一个臭名昭著的动态 SQL 问题。现在回到“if”示例,这次我们将“ACTIVE = 1”也设置成动态的条件,看看会发生什么。

    1. <select id="findActiveBlogLike"
    2. resultType="Blog">
    3. SELECT * FROM BLOG
    4. WHERE
    5. <if test="state != null">
    6. state = #{state}
    7. </if>
    8. <if test="title != null">
    9. AND title like #{title}
    10. </if>
    11. <if test="author != null and author.name != null">
    12. AND author_name like #{author.name}
    13. </if>
    14. </select>

    如果这些条件没有一个能匹配上会发生什么?最终这条 SQL 会变成这样:

    1. SELECT * FROM BLOG
    2. WHERE

    这会导致查询失败。如果仅仅第二个条件匹配又会怎样?这条 SQL 最终会是这样:

    1. SELECT * FROM BLOG
    2. WHERE
    3. AND title like someTitle

    这个查询也会失败。这个问题不能简单地用条件句式来解决,如果你也曾经被迫这样写过,那么你很可能从此以后都不会再写出这种语句了。

    MyBatis 有一个简单的处理,这在 90% 的情况下都会有用。而在不能使用的地方,你可以自定义处理方式来令其正常工作。一处简单的修改就能达到目的:

    1. <select id="findActiveBlogLike"
    2. resultType="Blog">
    3. SELECT * FROM BLOG
    4. <where>
    5. <if test="state != null">
    6. state = #{state}
    7. </if>
    8. <if test="title != null">
    9. AND title like #{title}
    10. </if>
    11. <if test="author != null and author.name != null">
    12. AND author_name like #{author.name}
    13. </if>
    14. </where>
    15. </select>

    where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除。

    如果 where 元素没有按正常套路出牌,我们可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

    1. <trim prefix="WHERE" prefixOverrides="AND |OR ">
    2. ...
    3. </trim>

    prefixOverrides 属性会忽略通过管道分隔的文本序列(注意此例中的空格也是必要的)。它的作用是移除所有指定在 prefixOverrides 属性中的内容,并且插入 prefix 属性中指定的内容。

    类似的用于动态更新语句的解决方案叫做 setset 元素可以用于动态包含需要更新的列,而舍去其它的。比如:

    1. <update id="updateAuthorIfNecessary">
    2. update Author
    3. <set>
    4. <if test="username != null">username=#{username},</if>
    5. <if test="password != null">password=#{password},</if>
    6. <if test="email != null">email=#{email},</if>
    7. <if test="bio != null">bio=#{bio}</if>
    8. </set>
    9. where id=#{id}
    10. </update>

    这里,set 元素会动态前置 SET 关键字,同时也会删掉无关的逗号,因为用了条件语句之后很可能就会在生成的 SQL 语句的后面留下这些逗号。(译者注:因为用的是“if”元素,若最后一个“if”没有匹配上而前面的匹配上,SQL 语句的最后就会有一个逗号遗留)

    若你对 set 元素等价的自定义 trim 元素的代码感兴趣,那这就是它的真面目:

    1. <trim prefix="SET" suffixOverrides=",">
    2. ...
    3. </trim>

    注意这里我们删去的是后缀值,同时添加了前缀值。