08-Mybatis 动态sql查询

动态 SQL 是 MyBatis 的强大特性之一。使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

MyBatis 动态 SQL 查询是一种可以根据不同的查询条件生成不同 SQL 语句的功能,方便实现灵活的查询操作。

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

一、 if

<if> 标签是 MyBatis 动态 SQL 查询中使用最为广泛的一个标签,在查询条件比较复杂时,能够有效地帮助我们生成所需的 SQL 语句。

1.使用条件判断语句生成 SQL 片段

<select id="getUserList" resultMap="userResultMap">
  SELECT * FROM user
  <where>
    <if test="name != null">
      AND name = #{name}
    </if>
    <if test="age != null">
      AND age = #{age}
    </if>
  </where>
</select>

在上述代码中,我们使用了 <if> 标签来生成 SQL 片段,该标签内的 test 属性值为条件判断语句,当符合条件时就会执行 SQL 语句块内的内容。在上述代码中,我们通过判断 name 和 age 是否为 null,来动态生成 SQL 条件语句。

2.使用逻辑运算符组合多个条件

在上述代码中,我们只使用了单个条件,但实际查询中,多条件联合查询的情况比较常见。为此,我们可以使用 <if> 标签结合逻辑运算符来组合多个条件,例如:

<select id="getUserList" resultMap="userResultMap">
  SELECT * FROM user
  <where>
    <if test="name != null and name != ''">
      AND name like concat('%', #{name}, '%')
    </if>
    <if test="age != null">
      AND age = #{age}
    </if>
    <if test="sex != null and sex != ''">
      AND sex = #{sex}
    </if>
  </where>
</select>

在上述代码中,我们使用了 and 关键字将多个条件进行逻辑组合,当同时满足所有条件时才会生成对应的 SQL 语句。

二、choose、when、otherwise

1.使用 <choose> 标签实现 switch 语句

常规的 switch 语句通常由多个 case 条件分支组成,根据变量的值来执行对应的代码。在 MyBatis 中,可以使用 <choose> 标签来实现类似 switch 语句的功能,其内部使用 <when> 标签表示每个条件分支,使用 <otherwise> 标签表示默认分支。

示例代码如下:

<select id="getUserList" resultMap="userResultMap">
  SELECT * FROM user
  <where>
    <choose>
      <when test="type == 1">
        AND age > #{age}
      </when>
      <when test="type == 2">
        AND sex = #{sex}
      </when>
      <otherwise>
        AND name like concat('%', #{name}, '%')
      </otherwise>
    </choose>
  </where>
</select>

在上述代码中,我们使用了 <choose> 标签来组合多个条件,并使用 <when> 标签表示当条件满足时需要生成的 SQL 语句,通过 test 属性来判断条件是否满足。在 <otherwise> 标签内的 SQL 语句是当所有条件都不满足时需要执行的默认 SQL 语句。

2.<when> 和 <otherwise> 标签的用法

除了使用 <choose> 标签实现 switch 语句外,我们还可以单独使用 <when> 或 <otherwise> 标签,它们分别表示条件成立时和条件不成立时需要生成的 SQL 片段。

示例代码如下:

<select id="getUserList" resultMap="userResultMap">
  SELECT * FROM user
  <where>
    <if test="name != null and name != ''">
      AND name like concat('%', #{name}, '%')
    </if>
    <when test="age > 18">
      AND age > 18
    </when>
    <otherwise>
      AND sex = 'male'
    </otherwise>
  </where>
</select>

在上述代码中,我们使用了 <when> 和 <otherwise> 标签,当满足条件时则生成对应的 SQL 语句片段,否则忽略该标签内的内容。需要注意的是,这种方式是将多个不同类型的标签混合使用,需要灵活掌握。

三、trim (where, set)

<trim> 标签可以在生成 SQL 语句时去掉多余或无效的关键字或分隔符。<trim> 标签的 prefix、prefixOverrides、suffix、suffixOverrides 和 suffixOverrides 属性非常灵活,可以实现去除多余的空格、逗号、AND/OR 等关键字等功能,提高 SQL 语句的可读性和执行效率。

其中 prefix 属性表示在 SQL 片段之前添加的字符串,prefixOverrides 属性用于去掉 SQL 片段内部多余的前缀字符串;类似的,suffix 属性用于在 SQL 片段之后添加的字符串,suffixOverrides 属性用于去掉 SQL 片段内部多余的后缀字符串,suffixOverrides 属性则用于去掉 SQL 片段的末尾和头部的多余分隔符。

下面我们分别介绍在查询语句和更新语句中,如何使用 <trim> 标签实现 WHERE 和 SET 子句的动态 SQL查询。

1.使用 <trim> 标签实现 WHERE 子句

<select id="getUserList" resultMap="userResultMap">
  SELECT * FROM user
  <where>
    <trim prefix="AND (" suffix=")">
      <if test="name != null and name != ''">
        name like concat('%', #{name}, '%')
        AND
      </if>
      <if test="age != null">
        age = #{age}
        AND
      </if>
      sex = #{sex}
    </trim>
  </where>
</select>

在上述代码中,我们使用了 <trim> 标签实现了一个带有 AND/OR 条件分支的 WHERE 子句。在 <trim> 标签中,prefix 属性用于添加前缀字符串,suffix 属性用于添加后缀字符串,当 <if> 标签中的 SQL 片段满足条件时,将会生成对应的 SQL 语句片段,并在其中添加多余的分隔符(这里是 AND)。在 <trim> 标签结束后,再去掉多余的分隔符(这里是 AND),从而得到一条完整的 WHERE 子句。

2.使用 <trim> 标签实现 SET 子句

<update id="updateUser" parameterType="User">
  UPDATE user
  <set>
    <trim suffixOverrides=",">
      <if test="name != null and name != ''">
        name = #{name},
      </if>
      <if test="age != null">
        age = #{age},
      </if>
      <if test="sex != null">
        sex = #{sex},
      </if>
    </trim>
  </set>
  WHERE id = #{id}
</update>

上述代码中,我们使用了 <trim> 标签实现了一个带有逗号分隔符的 SET 子句。在 <trim> 标签中,suffixOverrides 属性用于去掉 SQL 片段末尾的逗号分隔符。当 <if> 标签中的 SQL 片段满足条件时,将会生成对应的 SQL 语句片段,并在其中添加分隔符(这里是逗号)。在 <trim> 标签结束后,再去掉多余的逗号分隔符,从而得到一条完整的 SET 子句。

四、foreach

<foreach> 标签是 MyBatis 中常用的动态 SQL 标签之一,可以用来遍历一个集合或数组,并对其中的元素执行相同的 SQL 操作。在 <foreach> 标签中,我们可以使用 collection 属性指定被遍历的集合或数组,使用 item 属性指定遍历到的元素在 SQL 片段中的占位符,使用 open、close 和 separator 属性分别指定 SQL 片段的开头、结尾和分隔符。

下面我们分别介绍在查询语句和更新语句中,如何使用 <foreach> 标签实现动态 SQL 操作。

1.使用 <foreach> 标签实现 IN 子句

<select id="getUserList" resultMap="userResultMap">
  SELECT * FROM user
  WHERE id IN
  <foreach collection="idList" item="id" open="(" close=")" separator=",">
    #{id}
  </foreach>
</select>

在上述代码中,我们使用了 <foreach> 标签实现了一个带有 IN 子句的查询语句。在 <foreach> 标签中,collection 属性指定了 idList 集合参数的名称,item 属性指定了遍历时当前元素的占位符名称为 id,open 属性指定了插入的左侧括号,close 属性指定了插入的右侧括号,separator 属性指定了每个元素与前一个元素之间的分隔符为逗号。在遍历 idList 集合时,会将每个元素依次插入到 SQL 片段中,并用逗号分隔。

2.使用 <foreach> 标签实现批量插入

<insert id="batchInsertUser" parameterType="java.util.List">
  INSERT INTO user(name, age, sex)
  VALUES
  <foreach collection="list" item="user" separator=",">
    (#{user.name}, #{user.age}, #{user.sex})
  </foreach>
</insert>

上述代码中,我们使用了 <foreach> 标签实现了一个批量插入的 SQL 操作。在 <foreach> 标签中,collection 属性指定了传入参数 List 的名称,item 属性指定了遍历时当前元素的占位符名称为 user,separator 属性指定了每个插入语句之间的分隔符为逗号。在遍历 List 参数时,会将每个元素依次插入到 SQL 片段中,并用逗号分隔。