trim, where, set

The previous examples have been conveniently dancing around a notorious dynamic SQL challenge. Consider what would happen if we return to our “if” example, but this time we make “ACTIVE = 1” a dynamic condition as well.

  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>

What happens if none of the conditions are met? You would end up with SQL that looked like this:

  1. SELECT * FROM BLOG
  2. WHERE

This would fail. What if only the second condition was met? You would end up with SQL that looked like this:

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

This would also fail. This problem is not easily solved with conditionals, and if you’ve ever had to write it, then you likely never want to do so again.

MyBatis has a simple answer that will likely work in 90% of the cases. And in cases where it doesn’t, you can customize it so that it does. With one simple change, everything works fine:

  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>

The where element knows to only insert “WHERE” if there is any content returned by the containing tags. Furthermore, if that content begins with “AND” or “OR”, it knows to strip it off.

If the where element does not behave exactly as you like, you can customize it by defining your own trim element. For example, the trim equivalent to the where element is:

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

The prefixOverrides attribute takes a pipe delimited list of text to override, where whitespace is relevant. The result is the removal of anything specified in the prefixOverrides attribute, and the insertion of anything in the prefix attribute.

There is a similar solution for dynamic update statements called set. The set element can be used to dynamically include columns to update, and leave out others. For example:

  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>

Here, the set element will dynamically prepend the SET keyword, and also eliminate any extraneous commas that might trail the value assignments after the conditions are applied.

Alternatively, you can achieve the same effect by using trim element:

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

Notice that in this case we’re overriding a suffix, while we’re still appending a prefix.