MyBatis之动态SQL

时间:2025-03-27 08:01:29

动态SQL

动态SQL是MyBatis最强大的特性之一,它允许你根据条件动态拼接SQL语句,解决传统JDBC中大量拼接字符串的繁琐问题。

1. 什么是动态SQL?

动态SQL是一种根据运行时条件动态生成和执行SQL语句的技术。与静态SQL(在代码中固定写死的SQL)不同,动态SQL允许程序根据不同输入或条件动态灵活构建SQL,适用于需要灵活查询、参数化条件或复杂业务逻辑的场景。

  • 核心特点:通过条件判断、循环等逻辑,灵活拼接SQL字段,避免硬编码复杂的查询或更新逻辑。
  • 典型场景:多条件查询、批量操作、动态字段更新等。

2. 动态SQL的作用

  • 灵活适配业务需求:根据参数动态生成不同的SQL,避免写多个重复的静态SQL。
  • 减少代码冗余:通过逻辑控制标签(如if、foreach)简化代码。
  • 提升可维护性:集中管理SQL逻辑,修改时只需调整XML或模板文件。
  • 防止SQL注入:结合参数化查询(#{ }),避免直接拼接字符串的风险。

3. 动态SQL的常用标签

1. <if>标签:条件判断

作用:根据表达式的值(true/false)决定是否包含标签内的内容。

示例:根据传入参数动态更新字段。

<update id="updateUser">
    UPDATE user
    SET
        <if test="name != null">name = #{name},</if>
        <if test="age != null">age = #{age},</if>
        <if test="email != null">email = #{email}</if>
    WHERE id = #{id}
</update>

风险点:如果最后一个<if>条件不满足,SQL末尾会多一个逗号,导致语法错误!

错误SQL:

UPDATE user SET name = 'John', WHERE id = 1;  -- 逗号多余

2. <set>标签:动态更新字段(自动处理逗号)

作用:专用于处理UPDATE语句,自动去除SET子句末尾多余的逗号。

修正后的安全写法:

<update id="updateUser">
    UPDATE user
    <set>
        <if test="name != null">name = #{name},</if>
        <if test="age != null">age = #{age},</if>
        <if test="email != null">email = #{email},</if>
    </set>
    WHERE id = #{id}
</update>

MyBatis会智能生成:

-- 当只有 name 和 age 有值时
UPDATE user SET name = 'John', age = 25 WHERE id = 1; 

3. <trim>标签:自定义字符串处理(自定义修剪SQL片段的前缀、后缀)

作用:更灵活地处理前后缀,可替代<set>或<where>。

示例:用<trim>实现<set>的效果:

<trim prefix="SET" suffixOverrides=",">
    <if test="name != null">name = #{name},</if>
    <if test="age != null">age = #{age},</if>
</trim>

等同于<set>,但可以自定义前缀和要删除的后缀。

  • 属性及作用:
    • prefix:添加前缀
    • suffix:添加后缀
    • prefixOverride:去除开头匹配的字符串
    • suffixOverride:去除结尾匹配的字符串

4. <where>标签:智能处理where子句

作用:自动去除WHERE后的首个AND或OR,避免语法错误。

错误场景:

<select id="findUser">
    SELECT * FROM user
    WHERE
    <if test="name != null">AND name = #{name}</if>
    <if test="age != null">AND age = #{age}</if>
</select>

若所有条件都不满足,生成的SQL是:SELECT * FROM user WHERE (语法错误)。

修正后:

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

MyBatis会处理成:SELECT * FROM user WHERE name = 'John' 或直接去掉WHERE。

5. <foreach>标签:遍历集合或数组

作用:常用于IN查询或批量插入。

5.1 批量新增(List集合)

示例:根据id列表查询用户:

<insert id="batchInsert">
  INSERT INTO user (name, age) VALUES
  <foreach collection="list" item="user" separator=",">
    (#{user.name}, #{user.age})
  </foreach>
</insert>

生成SQL:select * from user where id in(1,2,3)

  • 参数说明:
    • collection="list":集合参数名(若参数是List类型,默认名为list)
    • item:遍历时的元素别名
    • separator:元素间的分隔符

5.2 批量删除(数组)
<delete id="batchDelete">
  DELETE FROM user WHERE id IN
  <foreach collection="array" item="id" open="(" separator="," close=")">
    #{id}
  </foreach>
</delete>
  • 参数说明:
    • collection="array":数组参数默认名为array。
    • open/close:包裹遍历结果的前后缀。

6. <choose>标签:

作用:实现多条件分支逻辑(类似switch-case)

<select id="findUser" resultType="User">
  SELECT * FROM user
  <where>
    <choose>
      <when test="name != null">
        name = #{name}
      </when>
      <when test="email != null">
        email = #{email}
      </when>
      <otherwise>
        status = 'ACTIVE'
      </otherwise>
    </choose>
  </where>
</select>