MyBatis 最强大的特性之一就是它的动态语句功能。如果您以前有使用 JDBC 或者类似框架的经历,您就会明白把 SQL 语句条件连接在一起是多么的痛苦,要确保不能忘记空格或者不要在 columns 列后面省略一个逗号等。动态语句能够完全解决掉这些痛苦。

尽管与动态 SQL 一起工作不是在开一个 party,但是 MyBatis 确实能通过在任何映射 SQL 语句中使用强大的动态 SQL 来改进这些状况。

动态 SQL 元素对于任何使用过 JSTL 或者类似于 XML 之类的文本处理器的人来说,都是非常熟悉的。在上一版本中,需要了解和学习非常多的元素,但在 MyBatis3 中有了许多的改进,现在只剩下差不多二分之一的元素。MyBatis 使用了基于强大的 OGNL 表达式来消除了大部分元素。

  • if
  • choose(when,otherwise)
  • trim(where,set)
  • foreach

一、if 元素

动态 SQL 最常做的事就是有条件地包括 where 子句。例如:

<select id="findActiveBlogWithTitleLike" parameterType="Blog" resultType="Blog">
    SELECT * FROM BLOG
    WHERE state='ACTIVE'
    <if test="title!=null">
        AND title like #{title}
    </if>
</select>

这条语句提供一个带功能性的可选的文字。如果您没有传入标题,那么将返回所有激活的 Blog。如果您传入了一个标题,那它就会查找与这个标题匹配的 Blog(在这种情况下,您的参数值可能需要包括任何 masking 或者通配符)。

如果我们想要可选地根据标题或者作者查询怎么办?首先,我把语句的名称稍稍改一下,使得看起来更直观。然后简单地加上另外一个条件。

<select id="findActiveBlogLike" parameterType="Blog" 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 title like #{author.name}
    </if>
</select>

二、choose,when,otherwise元素

有时候我们不想应用所有的条件,而是想从多个选项中选择一个。与 java 中的 switch 语句相似,MyBatis 提供了一个 choose 元素。

让我们继续使用上面的例子,但这次我们只搜索有提供查询标题的,或者只搜索有提供查询作者的数据。如果两者都没有提供,那只返回加精的 Blog(可能是管理员有选择性的查询,而不是返回大量无意义的随机的 Blog)。

<select id="findActiveBlogLike" parameterType="Blog" 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 title like #{author.name}
        </when>
        <otherwise>
            AND featured=1
        </otherwise>
    </choose>
</select>

三、trim,where,set元素

考虑一下我们上面提到的 'if' 的例子中,如果现在我们把'ACTIVE=1'也做为条件,会发生什么情况。

<select id="findActiveBlogLike" parameterType="Blog" 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 title like#{author.name}
    </if>
</select>

如果我们一个条件都不设置,会发生什么呢?语句最终可能会变成这个样子:

SELECT * FROM BLOG
WHERE

这将会执行失败。如果只有第二个条件满足呢?语句最终会变成这样:

SELECT * FROM BLOG
WHERE
AND title like 'someTitle'

这同样会执行失败。这个问题仅用条件很难简单地解决,如果您已经这么写了,那您可能以后永远都不想犯这样的错了。

MyBatis 有个简单的方案能解决这里面 90% 的问题。如果 where 没有出现的时候,您可以自定一个。修改一下,就能完全解决:

<select id="findActiveBlogLike" parameterType="Blog" 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 title like #{author.name}
        </if>
    </where>
</select>

where 元素知道插入 "where" 如果它包含的标签中有内容返回的话。此外,如果返回的内容以 "AND" 或者 "OR" 开头,它会把 "AND" 或者 "OR" 去掉。

如果 where 元素的行为并没有完全按您想象的那样,您还可以使用 trim 元素来自定义。例如,下面的 trim 与 where 元素实现相同功能:

<trim prefix="WHERE" prefixOverrides="AND|OR"></trim>

overrides 属性使用了管道分隔的文本列表来覆写,而且它的空白也不能忽略的。这样的结果是移出了指定在 overrides 属性里字符,而在开头插入 prefix 属性中指定的字符。

下面的两种配置方法效果是一样的:

<selectid="findActiveBlogLike" parameterType="Blog" 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 title like #{author.name}
        </if>
    </where>
</select>

<selectid="findActiveBlogLike" parameterType="Blog" resultType="Blog">
    SELECT * FROM BLOG
    <trim prefix="WHERE" prefixOverrides="AND |OR">
        <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 title like #{author.name}
        </if>
    </trim>
</select>

下面的使用 SET 元素也类似。

在动态 update 语句里相似的解决方式叫做 set,这个 set 元素能够动态地更新列。例如:

<update id="updateAuthorIfNecessary" parameterType="domain.blog.Author">
    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>
    whereid=#{id}
</update>

set 元素将动态的配置 SET 关键字,也用来剔除追加到条件末尾的任何不相关的逗号。

您想知道等同的 trim 元素该怎么写吧,它就像这样:

<trim prefix="SET" suffixOverrides=","></trim>

注意这种情况,我们剔除了一个后缀,同时追加了一个前缀。

四、Foreach 元素

另一个动态 SQL 经常使用到的功能是集合迭代,通常用在 IN 条件句。例如:

<select id="selectPostIn" resultType="domain.blog.Post">
    SELECT *
    FROM POST P
    WHERE ID in
    <foreach item="item" index="index" collection="list"
        open="("separator=","close=")">
        #{item}
    </foreach>
</select>

对上面这个映射 SQL 语句的 java 调用代码示例如下:

List<Integer> authorIdList = new ArrayList<Integer>();
postList.add(2);
postList.add(3);
postList.add(4);
List<Post> postList =(List<Post>)session.selectList("selectPostIn",postList);
//将会查询出ID是2、3、4的文章。

foreach 元素非常强大,允许您指定一个集合,申明能够用在元素体内的项和索引变量。也允许您指定开始和结束的字符,也可以加入一个分隔符到迭代器之间。这个元素的聪明之处在于它不会意外地追加额外的分隔符。

注意:您可以把一个 List 实例或者一个数组作为一个参数对象传递给 MyBatis。如果您这么做,MyBatis 会自动将它包装成一个 Map,并以名字作为 key。List 实例会以 "list" 作为key,array 实例会以 "array" 作为key。

 

相关文章:

  • 2021-07-31
  • 2022-02-07
  • 2022-12-23
  • 2021-12-27
  • 2021-10-31
  • 2022-02-03
  • 2022-12-23
  • 2022-03-06
猜你喜欢
  • 2022-02-11
  • 2021-10-31
  • 2021-08-16
  • 2021-12-18
相关资源
相似解决方案