MyBatis 中动态 SQL 的作用及执行原理解析

 更新时间:2026年02月26日 15:43:04   作者:莫寒清  
MyBatis动态SQL通过一组XML标签实现多条件查询、批量操作和按需更新等复杂场景,提升可读性和可维护性,并配合#{}防止SQL注入,下面通过本文给大家介绍MyBatis 中动态 SQL 的作用及执行原理解析,感兴趣的朋友跟随小编一起看看吧

一句话总结

MyBatis 动态 SQL 用一组 XML 标签(<if><choose><foreach><where><set> 等)在 Mapper 层按条件动态拼接 SQL,避免在 Java 代码里手动拼接字符串,实现多条件查询、批量操作和按需更新等复杂场景,提升可读性与可维护性,并配合 #{} 保证安全。

一、动态 SQL 的核心作用

1. 灵活的条件查询(多条件拼接WHERE)

根据参数是否为空,按需拼接 WHERE 条件,避免大量 if 拼接 SQL 字符串,以及多余的 AND / OR

<select id="findUser" resultType="User">
  SELECT * FROM user
  <where>
    <if test="name != null">
      AND name = #{name}
    </if>
    <if test="age != null">
      AND age = #{age}
    </if>
  </where>
</select>
  • nameage 都为空时:只生成 SELECT * FROM user
  • 只有 name 不为空:生成 SELECT * FROM user WHERE name = ?

面试点:动态 SQL 最典型用途之一就是多条件查询,根据参数是否为空动态增加 WHERE 条件。

2. 减少重复代码(替代多层 if-else 分支)

使用 <choose> / <when> / <otherwise> 做互斥分支选择,避免同一个 SQL 写多份、或者在 Java 里写一堆 if-else。

<select id="findUserByType" resultType="User">
  SELECT * FROM user
  WHERE role =
  <choose>
    <when test="role == 'admin'">
      'ADMIN'
    </when>
    <when test="role == 'user'">
      'USER'
    </when>
    <otherwise>
      'GUEST'
    </otherwise>
  </choose>
</select>
  • 优势
    • XML 中逻辑集中统一、互斥关系清晰;
    • 减少重复 SQL 片段,维护方便。

3. 动态批量操作(IN条件、批量插入 / 更新)

通过 <foreach> 遍历集合,动态生成 IN (...) 或批量插入的 VALUES (...) 列表。

<select id="selectByIds" resultType="User">
  SELECT * FROM user WHERE id IN
  <foreach collection="ids"
           item="id"
           open="("
           separator=","
           close=")">
    #{id}
  </foreach>
</select>
  • 典型场景:批量查询、批量更新、批量删除等,减少数据库往返次数、提升性能。

4. 配合参数绑定,降低 SQL 注入风险

通过 #{} 占位符和 <bind> 动态生成参数值,防止将用户输入直接拼到 SQL 里。

<select id="findByNameLike" resultType="User">
  <bind name="pattern" value="'%' + name + '%'" />
  SELECT * FROM user
  WHERE name LIKE #{pattern}
</select>
  • 即使参数中包含 ' OR 1=1 --,也会被当成普通字符串,而不是 SQL 片段执行。
  • 注意:真正防注入的关键在于 #{} + 预编译,动态 SQL 只是避免你再去拼字符串

5. 动态更新与字段控制(按需更新非空字段)

使用 <set> 标签只更新非空字段,并自动处理多余逗号。

<update id="updateUser">
  UPDATE user
  <set>
    <if test="name != null">
      name = #{name},
    </if>
    <if test="age != null">
      age = #{age},
    </if>
  </set>
  WHERE id = #{id}
</update>
  • 只会更新有值的字段,避免把字段更新成 NULL
  • <set> 会自动去掉最后一个逗号,生成合法 SQL。

二、典型应用场景(面试可以照着说)

场景动态 SQL 标签组合效果
多条件查询<if> + <where>按需生成 WHERE 子句,避免冗余条件
分页 + 条件查询<if> + <where> + 分页插件条件 + 分页统一在一个 Mapper 中维护
批量插入 / 更新<foreach>单条 SQL 处理多条数据,减少数据库交互
多表关联查询<choose> 动态选表 / 关联根据业务条件选择不同的关联方式或表
动态排序<if> 控制 ORDER BY支持前端传入排序字段、排序方式,实现灵活排序

一句话记忆:动态 SQL 常用在多条件查询、批量操作、动态排序、部分字段更新、复杂关联这几类场景。

三、动态 SQL 的执行原理(加分项)

  • OGNL 表达式解析
    • MyBatis 使用 OGNL(Object-Graph Navigation Language)解析 <if test="..."><foreach> 等标签中的条件表达式;
    • 通过 test="name != null and name != ''" 这类表达式判断是否拼接对应的 SQL 片段。
  • SqlNode 组合成 SqlSource
    • 所有动态标签先被解析成一棵 SqlNode 树(比如 IfSqlNodeWhereSqlNode 等);
    • 运行时根据入参在这棵树上“走一遍”,把满足条件的节点拼接成完整 SQL,生成 SqlSource
  • 预编译与参数绑定
    • 最终 SQL 仍然通过 PreparedStatement 预编译;
    • #{} 会变成 ? 占位符,参数通过 JDBC 安全绑定,既防注入又可复用执行计划。

四、实际场景示例:多条件搜索用户

<select id="searchUsers" resultType="User">
  SELECT * FROM user
  <where>
    <if test="name != null and name != ''">
      AND name LIKE CONCAT('%', #{name}, '%')
    </if>
    <if test="minAge != null">
      AND age >= #{minAge}
    </if>
    <if test="roles != null and roles.size() > 0">
      AND role IN
      <foreach collection="roles"
               item="role"
               open="("
               close=")"
               separator=",">
        #{role}
      </foreach>
    </if>
  </where>
  ORDER BY create_time DESC
</select>
  • 支持:模糊查询、年龄下限过滤、角色集合过滤;
  • 传哪个条件就拼哪个条件,未传的不生效;
  • 所有入参都走 #{},安全、可预编译。

五、面试回答模板(直接背)

  • 问:MyBatis 动态 SQL 有什么作用?常用在哪些场景?
    • 答:“MyBatis 通过 <if><choose><foreach><where><set> 等标签,在 XML 里按条件动态拼接 SQL,代替 Java 代码里手动拼字符串。常用在多条件查询、批量插入 / 更新、动态排序、按需更新部分字段、复杂关联查询等场景,既提高了可读性和维护性,又能配合 #{} 和预编译保证安全。”

到此这篇关于MyBatis 中动态 SQL 的作用及执行原理解析的文章就介绍到这了,更多相关mybatis动态sql使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java ThreadLocal线程局部变量常用方法使用场景示例详解

    java ThreadLocal线程局部变量常用方法使用场景示例详解

    这篇文章主要介绍了为大家java ThreadLocal线程局部变量常用方法使用场景示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-07-07
  • JAVAWEB实现简单的商城项目(一)实例代码解析

    JAVAWEB实现简单的商城项目(一)实例代码解析

    本文给大家分享一段实例代码给大家介绍JAVAWEB实现简单的商城项目(一),非常具有参考价值,感兴趣的朋友一起学习吧
    2016-02-02
  • spring cloud gateway 如何修改请求路径Path

    spring cloud gateway 如何修改请求路径Path

    这篇文章主要介绍了spring cloud gateway 修改请求路径Path的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Java项目中大批量数据查询导致OOM的解决

    Java项目中大批量数据查询导致OOM的解决

    本文主要介绍了Java项目中大批量数据查询导致OOM的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • Java中初始化块详解及实例代码

    Java中初始化块详解及实例代码

    这篇文章主要介绍了Java中初始化块详解及实例代码的相关资料,在Java中,有两种初始化块:静态初始化块和非静态初始化块,需要的朋友可以参考下
    2017-03-03
  • java设计模式之浅谈适配器模式

    java设计模式之浅谈适配器模式

    我们现实生活中的适配器不少.例如,我们使用存储卡适配器连接存储卡和一个计算机,因为计算机仅支持一种类型的存储卡和我们的卡不与计算机兼容,适配器是两种不相容的实体之间的转换器,适配器模式是一种结构模式.本文就带大家了解一下java适配器模式,需要的朋友可以参考下
    2021-06-06
  • java基础之方法和方法的重载详解

    java基础之方法和方法的重载详解

    这篇文章主要介绍了java基础之方法和方法的重载详解,文中有非常详细的代码示例,对正在学习java基础的小伙伴们有很好的帮助,需要的朋友可以参考下
    2021-05-05
  • Java super关键字的使用详解

    Java super关键字的使用详解

    java中的super关键字是一个引用变量,用于引用直接父类对象,下面这篇文章主要给大家介绍一下super关键字的使用,需要的朋友可以参考下
    2022-07-07
  • 一篇文章带你玩转Java EasyExcel(Excel报表必学)

    一篇文章带你玩转Java EasyExcel(Excel报表必学)

    EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目,本文主要为大家详细介绍了EasyExcel操作Excel的相关操作,希望对大家有所帮助
    2025-08-08
  • java多线程抓取铃声多多官网的铃声数据

    java多线程抓取铃声多多官网的铃声数据

    很容易就能发现通过改变 listId和page就能从服务器获取铃声的json数据, 通过解析json数据, 可以看到都带有{"hasmore":1,"curpage":1}这样子的指示,通过判断hasmore的值,决定是否进行下一页的抓取。 但是通过上面这个链接返回的json中不带有铃声的下载地址
    2016-04-04

最新评论