• MyBatis(4):动态SQL


    什么是动态SQL

    MyBatis的一个强大特性之一通常是它的动态SQL能力。如果你有使用JDBC或其他相似框架的经验,你就明白条件串联SQL字符串在一起是多么地痛苦,确保不能忘了空格或者在列表的最后的省略逗号,动态SQL可以彻底处理这种痛苦。

    通常使用动态SQL不可能是独立的一部分,MyBatis当然使用一种强大的动态SQL语言来改进这种情形,这种语言可以被用在任意映射的SQL语句中。

    动态SQL元素和使用JSTL或其它相似的基于XML的文本处理器相似,在MyBatis之前的版本中,有很多元素需要了解,MyBatis3大大地提升了它们,现在用不到原先一半的元素就能工作了,MyBatis采用功能强大的基于OGNL的表达式来消除其他元素。

    OK,介绍就到这儿,下面来进入动态SQL的学习吧。

    if

    在动态SQL中所做的最通用的事情就是包含部分where子句的条件,比如:

    1
    2
    3
    4
    5
    6
    <select id="selectInCondition" parameterType="student" resultType="student">
        select * from student where studentId > #{studentId}
        <if test="studentName != null">
            and studentName = #{studentName};
        </if>
    </select>

    具体实现不写了,那么如果我这么调用:

    1
    List<Student> list = StudentOperator.getInstance().selectInCondition(0, "Jack", 0, null);

    查询的就是studentId>0且studentName=”Jack”的所有学生信息,如果换一种调用方式:

    1
    List<Student> list = StudentOperator.getInstance().selectInCondition(0, null, 0, null);

    那么查询的就是studentId>0的所有学生信息。

    多个where子句也是一样的,比如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <select id="selectInCondition" parameterType="student" resultType="student">
        <![CDATA[
            select * from student where studentId > #{studentId}
        ]]>
        <if test="studentName != null and studentName != 'Jack' ">
            and studentName = #{studentName}
        </if>
        <if test="studentAge != 0">
            and studentAge = #{studentAge};
        </if>
    </select>

    注意一下,能用”<![CDATA[ ... ]]>”尽量还是用,不过只包动态SQL外的内容。

    另外,test里面可以判断字符串、整型、浮点型,大胆地写判断条件吧。如果属性是复合类型,则可以使用A.B的方式去获取复合类型中的属性来进行比较。

    choose、when、otherwise

    有时候我们不想应用所有的应用条件,相反我们想选择很多情况下的一种。和Java中的switch…case…类似,MyBasit提供choose元素。

    上面的例子是两种if判断都可能存在,接下来使用choose、when、other做一些修改:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <select id="selectInCondition" parameterType="student" resultType="student">
        <![CDATA[
            select * from student where studentId > #{studentId}
        ]]>
        <choose>
            <when test="studentName != null">
                and studentName = #{studentName};
            </when>
            <when test="studentAge != 0">
                and studentAge = #{studentAge};
            </when>
            <otherwise>
                or 1 = 1;
            </otherwise>
        </choose>
    </select>

    两个when只能满足一个,都不满足则走other。还是注意一下这里的”<![CDATA[ ... ]]>”,不可以包围整个语句。

    trim、where、set

    第一个例子已经示例了if的用法,但是这种用法有个缺陷—-动态SQL外必须有where子句。

    什么意思,因为很多时候我们需要where后面的子句都动态生成,而不是事先有一个where,这样就有问题,比如说:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <select id="selectInCondition" parameterType="student" resultType="student">
        <![CDATA[
            select * from student where
        ]]>
        <if test="studentName != null and studentName != 'Jack' ">
            and studentName = #{studentName}
        </if>
        <if test="studentAge != 0">
            and studentAge = #{studentAge};
        </if>
    </select>

    如果所有条件都不匹配,那么生成的SQL语句将是:

    1
    select * from student where

    这将导致查询失败。即使只满足一个查询条件还是有问题,比如满足studentName那个吧,生成的SQL语句将是:

    1
    select * from student where and studentName = #{studentName};

    这个查询也会失败。

    解决办法也有,一个讨巧的办法是用where 1 = 1的方式,即:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <select id="selectInCondition" parameterType="student" resultType="student">
        <![CDATA[
            select * from student where 1 = 1
        ]]>
        <if test="studentName != null and studentName != 'Jack' ">
            and studentName = #{studentName}
        </if>
        <if test="studentAge != 0">
            and studentAge = #{studentAge};
        </if>
    </select>

    因为”1 = 1″永远满足,所以相当于给where加了一层true而已,此时动态SQL生成什么where判断条件就是什么。

    另外一个解决办法是利用MyBatis中的一个简单处理方式,这在90%情况下都会有用而且。而在不能使用的地方,可以以自定义方式处理。加上一个简单的改变,所有的事情都会顺利进行:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <select id="selectInCondition" parameterType="student" resultType="student">
        <![CDATA[
            select * from student
        ]]>
        <where>
            <if test="studentName != null and studentName != 'Jack' ">
                and studentName = #{studentName}
            </if>
            <if test="studentAge != 0">
                and studentAge = #{studentAge};
            </if>
        </where>
    </select>

    where元素知道如果由被包含的标记返回任意内容,就仅仅插入where。而且,如果以”and”或”or”开头的内容,那么就会跳过where不插入。

    如果where元素没有做出你想要的,那么可以使用trim元素来自定义。比如,和where元素相等的trim元素是:

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

    即:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <select id="selectInCondition" parameterType="student" resultType="student">
        select * from student
        <trim prefix="WHERE" prefixOverrides="AND |OR ">
            <if test="studentName != null and studentName != 'Jack' ">
                and studentName = #{studentName}
            </if>
            <if test="studentAge != 0">
                and studentAge = #{studentAge};
            </if>
        </trim>
    </select>

    特别要注意,prefixOverrides中的空白也是很重要的

    最后一个小内容,和动态更新语句相似的解决方案是set。set元素可以被用于动态包含更新的列,而不包含不需要更新的。比如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <update id="updateStudentAgeById" parameterType="Student">
        <!--update student set studentAge = #{studentAge} where
            studentId = #{studentId}; -->
        <![CDATA[
            update student
        ]]>
        <set>
            <if test="studentAge != 0">studentAge = #{studentAge}</if>
        </set>
        where studentId = #{studentId}
    </update>

    可以对比一下,注释掉的是原update语句,没有注释的是加入动态SQL之后的语句。

    这里,set元素会动态前置set关键字,而且也会消除任意无关的逗号。如果你对和这里对等的trim元素好奇,它看起来是这样的:

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

    这种时候我们附加一个后缀,同时也附加一个前缀。

    foreach

    另外一个动态SQL通用的必要操作时迭代一个集合,通常是构建在in条件中的。比如(上面的例子都是我在自己电脑上跑通过的例子,这个例子就直接复制MyBatis官方文档上的内容了):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <select id="selectPostIn" resultType="domain.blog.Post">
        <![CDATA[
            SELECT * FROM POST P WHERE ID in
        ]]>
        <foreach item="item" index="index" collection="list"
            open="(" separator="," close=")">
            #{item}
        </foreach>
    </select>

    foreach是非常强大的,它允许你指定一个集合,声明集合项和索引变量,它们可以用在元素体内。他也允许你指定开放和关闭字符串,在迭代之间放置分隔符。这个元素是很智能的,它不会偶然地附加多余的分隔符。

  • 相关阅读:
    hdu 1823 Luck and Love 二维线段树
    UVA 12299 RMQ with Shifts 线段树
    HDU 4578 Transformation 线段树
    FZU 2105 Digits Count 线段树
    UVA 1513 Movie collection 树状数组
    UVA 1292 Strategic game 树形DP
    【ACM】hdu_zs2_1003_Problem C_201308031012
    qsort快速排序
    【ACM】nyoj_7_街区最短路径问题_201308051737
    【ACM】nyoj_540_奇怪的排序_201308050951
  • 原文地址:https://www.cnblogs.com/tuojunjie/p/6210286.html
Copyright © 2020-2023  润新知