zoukankan      html  css  js  c++  java
  • 动态SQL(Dynamic SQL)

    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。

  • 相关阅读:
    数据结构(2)-链表
    数据结构(1)-数组
    SpringMVC学习总结(一)--Hello World入门
    基本数据类型对象的包装类
    关于String的相关常见方法
    常见的集合容器应当避免的坑
    再一次生产 CPU 高负载排查实践
    分表后需要注意的二三事
    线程池没你想的那么简单(续)
    线程池没你想的那么简单
  • 原文地址:https://www.cnblogs.com/jwen1994/p/10923908.html
Copyright © 2011-2022 走看看