MyBatis 动态 SQL:`if`, `where`, `choose`, `foreach` 等标签的应用

MyBatis 动态 SQL:让你的数据库查询也玩起“变形金刚”!

各位看官,今天咱们来聊聊 MyBatis 里面的“变形金刚”——动态 SQL!啥是动态 SQL?简单来说,就是让你的 SQL 语句不再死板,可以根据不同的条件“变身”成不同的模样,就像变形金刚一样,能适应各种战斗环境!

在 MyBatis 里,实现动态 SQL 的利器就是一系列的标签,比如 <if>, <where>, <choose>, <foreach> 等等。 它们就像是变形金刚的各个关节,通过巧妙的组合,就能让你的 SQL 语句拥有无限的可能。

接下来,咱们就深入了解一下这些“变形金刚”的核心部件,看看它们各自都有哪些绝活!

1. <if> 标签:SQL 语句的“条件判断器”

<if> 标签的作用非常简单直接,就像编程语言里的 if 语句一样,它会根据指定的条件来决定是否将一段 SQL 片段包含到最终的 SQL 语句中。

语法格式:

<if test="条件表达式">
  SQL 片段
</if>

举个栗子:

假设我们有一个 User 类,包含 id, username, email 三个字段。现在我们需要根据 username 是否为空来决定是否要进行模糊查询。

public class User {
  private Integer id;
  private String username;
  private String email;

  // 省略 getter 和 setter 方法
}

对应的 Mapper 接口:

public interface UserMapper {
  List<User> findUsers(User user);
}

Mapper XML 文件:

<select id="findUsers" parameterType="User" resultType="User">
  SELECT * FROM users
  WHERE 1 = 1 <!-- 这是一个小技巧,保证 WHERE 后面始终有条件 -->
  <if test="username != null and username != ''">
    AND username LIKE CONCAT('%', #{username}, '%')
  </if>
</select>

在这个例子中,如果 user 对象的 username 属性不为空,那么 AND username LIKE CONCAT('%', #{username}, '%') 这段 SQL 片段就会被添加到最终的 SQL 语句中。否则,这段 SQL 片段就会被忽略。

注意事项:

  • test 属性的值是一个 OGNL 表达式。OGNL (Object-Graph Navigation Language) 是一种强大的表达式语言,MyBatis 使用 OGNL 来访问对象的属性。
  • test 表达式中,可以使用 null, ==, !=, and, or, not 等运算符。
  • 字符串的判空,建议使用 != null and username != '',这样可以同时处理 null 和空字符串的情况。

2. <where> 标签:让你的 WHERE 子句更优雅

<where> 标签是一个非常实用的标签,它可以智能地处理 WHERE 子句的开头,自动添加 WHERE 关键字,并且可以智能地移除多余的 ANDOR 关键字。

语法格式:

<where>
  SQL 片段
</where>

举个栗子:

还是上面的 User 类,现在我们需要根据 usernameemail 来进行查询,但是这两个条件都是可选的。

Mapper XML 文件:

<select id="findUsers" parameterType="User" resultType="User">
  SELECT * FROM users
  <where>
    <if test="username != null and username != ''">
      AND username LIKE CONCAT('%', #{username}, '%')
    </if>
    <if test="email != null and email != ''">
      AND email = #{email}
    </if>
  </where>
</select>

在这个例子中,如果 usernameemail 都为空,那么最终的 SQL 语句就是 SELECT * FROM users。如果 username 不为空,而 email 为空,那么最终的 SQL 语句就是 SELECT * FROM users WHERE username LIKE CONCAT('%', #{username}, '%')。如果 usernameemail 都不为空,那么最终的 SQL 语句就是 SELECT * FROM users WHERE username LIKE CONCAT('%', #{username}, '%') AND email = #{email}

看到了吗?<where> 标签自动帮我们处理了 WHERE 关键字和多余的 AND 关键字,让我们的 SQL 语句更加简洁优雅!

原理:

<where> 标签会判断其内部的 SQL 片段是否包含任何内容。如果包含内容,它就会自动添加 WHERE 关键字,并且移除第一个 ANDOR 关键字。

3. <choose>, <when>, <otherwise> 标签:SQL 语句的“多路选择器”

<choose>, <when>, <otherwise> 标签组合起来,就像编程语言里的 switch...case...default 语句一样,可以根据不同的条件选择不同的 SQL 片段。

语法格式:

<choose>
  <when test="条件表达式1">
    SQL 片段1
  </when>
  <when test="条件表达式2">
    SQL 片段2
  </when>
  ...
  <otherwise>
    SQL 片段
  </otherwise>
</choose>

举个栗子:

还是上面的 User 类,现在我们需要根据不同的条件来排序用户:

  • 如果 sortByUsernametrue,则按照 username 排序。
  • 如果 sortByEmailtrue,则按照 email 排序。
  • 否则,按照 id 排序。

Mapper XML 文件:

<select id="findUsers" parameterType="Map" resultType="User">
  SELECT * FROM users
  ORDER BY
  <choose>
    <when test="sortByUsername == true">
      username
    </when>
    <when test="sortByEmail == true">
      email
    </when>
    <otherwise>
      id
    </otherwise>
  </choose>
</select>

在这个例子中,我们使用了 Map 作为参数类型,这样可以方便地传递多个参数。

注意事项:

  • <choose> 标签中只能包含 <when><otherwise> 标签。
  • <when> 标签的 test 属性的值是一个 OGNL 表达式。
  • <otherwise> 标签是可选的,如果省略了 <otherwise> 标签,并且所有的 <when> 标签的条件都不满足,那么最终的 SQL 语句就不会包含任何排序条件。
  • <choose> 标签只会选择第一个条件满足的 <when> 标签,如果多个 <when> 标签的条件都满足,那么只有第一个 <when> 标签对应的 SQL 片段会被执行。

4. <foreach> 标签:处理集合的“循环利器”

<foreach> 标签可以用来循环遍历集合,并根据集合中的元素生成 SQL 片段。它常用于 IN 子句、批量插入、批量更新等场景。

语法格式:

<foreach collection="集合" item="元素" index="索引" separator="分隔符" open="开始符号" close="结束符号">
  SQL 片段
</foreach>

属性说明:

  • collection: 要遍历的集合。可以是 List, Set, Map, 数组等。
  • item: 集合中每个元素的别名。
  • index: 集合中每个元素的索引(仅对 List 和数组有效)。
  • separator: 每个元素之间的分隔符。
  • open: 循环开始时的符号。
  • close: 循环结束时的符号。

举个栗子:

假设我们有一个 List<Integer> 类型的 ids,现在我们需要查询 idids 列表中的所有用户。

Mapper 接口:

public interface UserMapper {
  List<User> findUsersByIds(List<Integer> ids);
}

Mapper XML 文件:

<select id="findUsersByIds" parameterType="java.util.List" resultType="User">
  SELECT * FROM users
  WHERE id IN
  <foreach collection="list" item="id" separator="," open="(" close=")">
    #{id}
  </foreach>
</select>

在这个例子中,collection="list" 表示要遍历的集合是 List 类型的参数。item="id" 表示集合中每个元素的别名是 idseparator="," 表示每个元素之间用逗号分隔。open="(" 表示循环开始时添加一个左括号。close=")" 表示循环结束时添加一个右括号。

如果 ids 列表中的元素是 [1, 2, 3],那么最终的 SQL 语句就是 SELECT * FROM users WHERE id IN (1, 2, 3)

再来一个批量插入的栗子:

现在我们需要批量插入一批用户。

Mapper 接口:

public interface UserMapper {
  int insertUsers(List<User> users);
}

Mapper XML 文件:

<insert id="insertUsers" parameterType="java.util.List">
  INSERT INTO users (username, email) VALUES
  <foreach collection="list" item="user" separator=",">
    (#{user.username}, #{user.email})
  </foreach>
</insert>

在这个例子中,collection="list" 表示要遍历的集合是 List 类型的参数。item="user" 表示集合中每个元素的别名是 userseparator="," 表示每个元素之间用逗号分隔。

注意事项:

  • 如果 collection 属性的值是 listarray,那么 MyBatis 会自动将参数转换为 List 或数组。
  • 如果 collection 属性的值是 map,那么 item 属性的值就是 map 中每个 value 的别名,index 属性的值就是 map 中每个 key 的别名。
  • 在使用 <foreach> 标签时,需要注意 SQL 语句的长度限制,避免 SQL 语句过长导致数据库执行失败。

5. <set> 标签:让你的 UPDATE 语句更灵活

<set> 标签类似于 <where> 标签,它可以智能地处理 UPDATE 语句的开头,自动添加 SET 关键字,并且可以智能地移除多余的 , 逗号。

语法格式:

<set>
  SQL 片段
</set>

举个栗子:

现在我们需要根据 id 更新 User 对象的 usernameemail 属性,但是这两个属性都是可选的。

Mapper XML 文件:

<update id="updateUser" parameterType="User">
  UPDATE users
  <set>
    <if test="username != null and username != ''">
      username = #{username},
    </if>
    <if test="email != null and email != ''">
      email = #{email},
    </if>
  </set>
  WHERE id = #{id}
</update>

在这个例子中,如果 usernameemail 都为空,那么最终的 SQL 语句就是 UPDATE users SET WHERE id = #{id} (这条语句会报错,因为 SET 后面没有内容,实际使用中需要至少有一个字段更新)。如果 username 不为空,而 email 为空,那么最终的 SQL 语句就是 UPDATE users SET username = #{username} WHERE id = #{id}。如果 usernameemail 都不为空,那么最终的 SQL 语句就是 UPDATE users SET username = #{username}, email = #{email} WHERE id = #{id}

看到了吗?<set> 标签自动帮我们处理了 SET 关键字和多余的 , 逗号,让我们的 SQL 语句更加简洁优雅!

原理:

<set> 标签会判断其内部的 SQL 片段是否包含任何内容。如果包含内容,它就会自动添加 SET 关键字,并且移除最后一个 , 逗号。

6. <bind> 标签:给你的变量起个“新名字”

<bind> 标签可以用来创建一个新的变量,并将其绑定到一个 OGNL 表达式的值。这在一些复杂的查询场景中非常有用,可以提高 SQL 语句的可读性和可维护性。

语法格式:

<bind name="变量名" value="OGNL 表达式"/>

举个栗子:

假设我们需要根据 username 进行模糊查询,但是我们希望在 SQL 语句中使用一个更短的变量名。

Mapper XML 文件:

<select id="findUsers" parameterType="User" resultType="User">
  <bind name="usernameLike" value="'%' + username + '%'"/>
  SELECT * FROM users
  WHERE username LIKE #{usernameLike}
</select>

在这个例子中,我们使用 <bind> 标签创建了一个名为 usernameLike 的变量,并将其绑定到 '%'+username+'%' 这个 OGNL 表达式的值。这样,我们就可以在 SQL 语句中使用 #{usernameLike} 来代替 CONCAT('%', #{username}, '%'),使 SQL 语句更加简洁易懂。

总结:

标签 功能 适用场景
<if> 条件判断,根据条件决定是否包含 SQL 片段 各种需要根据条件动态拼接 SQL 的场景
<where> 智能处理 WHERE 子句,自动添加 WHERE 关键字,并移除多余的 AND 或 OR 关键字 需要动态拼接 WHERE 子句,且条件可选的场景
<choose> 多路选择,根据不同的条件选择不同的 SQL 片段 需要根据不同的条件选择不同的 SQL 片段的场景,类似于 switch…case…default 语句
<foreach> 循环遍历集合,根据集合中的元素生成 SQL 片段 处理 IN 子句、批量插入、批量更新等需要循环遍历集合的场景
<set> 智能处理 UPDATE 语句,自动添加 SET 关键字,并移除多余的 , 逗号 需要动态拼接 UPDATE 语句,且更新字段可选的场景
<bind> 创建一个新的变量,并将其绑定到一个 OGNL 表达式的值 提高 SQL 语句的可读性和可维护性,尤其是在复杂的查询场景中

最佳实践:

  • 尽量使用 <where><set> 标签,避免手动处理 WHERE 子句和 SET 子句的开头和结尾。
  • 使用 <bind> 标签来简化复杂的 OGNL 表达式,提高 SQL 语句的可读性。
  • 注意 SQL 语句的长度限制,避免 SQL 语句过长导致数据库执行失败。
  • 在编写动态 SQL 时,要充分考虑各种边界情况,确保 SQL 语句的正确性和安全性。

好了,各位看官,关于 MyBatis 动态 SQL 的介绍就到这里了。希望这篇文章能够帮助大家更好地理解和使用 MyBatis 的动态 SQL 功能,让你的数据库查询也玩起“变形金刚”! 记住,灵活运用这些标签,你就能像一位经验丰富的 SQL 魔法师,轻松驾驭各种复杂的查询需求!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注