Mybatis的动态SQL

这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战


MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
虽然在以前使用动态 SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。
动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。

IF 语句

<!--
如果名字为空,那么只根据密码查询,反之,则根据名字来查询
select * from blog where name = #{name} and password = #{password}
-->
<select id="selectUserByNameAndPassword" parameterType="map" resultType="pojo.User">
    select *
    from user
    where
    <if test="name!=null">
        name = #{name}
    </if>
    <if test="password!=null">
        and password = #{password}
    </if>
</select>
复制代码

上述语句中,如果 title 为空那么查询语句为 select * from user where and password = #{password} ,这是错误的 SQL 语句

Where 语句

<!--
如果名字为空,那么只根据密码查询,反之,则根据名字来查询
select * from blog where name = #{name} and password = #{password}
-->
<select id="selectUserByNameAndPassword" parameterType="map" resultType="pojo.User">
    select *
    from user
    <where>
        <if test="name != null">
            name = #{name}
        </if>
        <if test="password != null">
            and password = #{password}
        </if>
    </where>
</select>
复制代码

“where”标签:如果它包含的标签中有返回值的话,它就会插入一个‘where’。此外,如果标签返回的内容是以 AND 或 OR 开头的,则它会将其剔除掉。

Set 语句

<!--
update user set name=#{name},password=#{password} where id=#{id}
-->
<update id="updateUserById" parameterType="pojo.User">
    update user
    <set>
        <if test="name!=null">
            name=#{name},
        </if>
        <if test="password!=null">
            password = #{password}
        </if>
    </set>
    where id = #{id}
</update>
复制代码

Choose语句

类似于 Java 的 switch 语句,查询条件有一个满足即可。


<select id="selectUserByNameAndPassword" parameterType="map" resultType="pojo.User">
    select *
    from user
    <where>
        <choose>
            <when test="name != null">
                name = #{name}
            </when>
            <when test="password != null">
                and password = #{password}
            </when>
            <otherwise>
                and id=#{id}
            </otherwise>
        </choose>
    </where>
</select>
复制代码

foreach

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

动态 SQL 的另外一个常用的必要操作是需要对一个集合进行遍历,通常是在构建 IN 条件语句的时候。比如:

<!--select * from user where id in (1,2,3)-->
<select id="selectUser" resultType="pojo.User">
    select *
    from user
    where id in
    <!--
        List 实例将会以 "list" 作为键
        数组实例的键是 "array"
        -->
    <foreach collection="array" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>
复制代码
  • collection:指定输入对象中的集合属性
  • item:每次遍历生成的对象
  • open:开始遍历时的拼接字符串
  • close:结束时拼接的字符串
  • separator:遍历对象之间需要拼接的字符串

注意:可以将一个 List 实例或者数组作为参数对象传给 MyBatis,当你这么做的时候,MyBatis 会自动将它包装在一个 Map 中并以名称为键。List 实例将会以 "list" 作为键,而数组实例的键将是 "array"。

SQL片段

有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。

  • 提取重复 SQL 片段:

    <sql id="if-name-password">
        <if test="name != null">
            name = #{name}
        </if>
        <if test="password != null">
            and password = #{password}
        </if>
    </sql>
    复制代码
  • 引用SQL片段:

    <select id="selectUserByNameAndPassword" parameterType="map" resultType="pojo.User">
      select * from user
       <where>
           <!-- 引用 sql 片段,如果 refid 指定的不在本文件中,那么需要在前面加上 namespace -->
           <include refid="if-name-password"></include>
           <!-- 在这里还可以引用其他的 sql 片段 -->
       </where>
    </select>
    复制代码

注意:

  1. 最好基于单表来定义 sql 片段,提高片段的可重用性
  2. 在 sql 片段中不要包括 where