news 2025/12/19 23:53:09

《MyBatis 动态 SQL 天花板教程:吃透 6 大标签,搞定所有动态拼接场景》

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
《MyBatis 动态 SQL 天花板教程:吃透 6 大标签,搞定所有动态拼接场景》

一、动态SQL

动态SQL是Mybatis的强⼤特性之⼀,能够完成不同条件下不同的sql拼接

可以参考官⽅⽂档:Mybatis动态SQL

1.1动态 SQL 的<if>标签用法

(1)为什么要用动态 SQL?

在业务场景中(比如注册用户),存在必填字段(比如用户名、密码)和非必填字段(比如性别、生日)。如果直接写固定 SQL,非必填字段没传值时,会出现两种问题:

  • 要么 SQL 语法错误(比如多了逗号);
  • 要么把数据库字段的默认值覆盖成null(如果字段有默认值的话)。

所以 MyBatis 的动态 SQL 可以根据参数是否存在,自动拼接 / 忽略对应的 SQL 片段

(2)<if>标签的核心逻辑

<if test="条件">...</if>表示:test里的条件满足时,才会把<if>包裹的 SQL 片段拼接到最终 SQL 中

以 “注册用户” 为例,gender非必填字段,所以用<if>判断:

  • 当传入的userInfo对象中gender != null时,才拼接gender对应的字段和值;
  • gender == null时,自动忽略这部分 SQL。

(3)代码示例拆解

以 “插入用户” 的insertUserByCondition为例:

1. 接口定义

这里传入的UserInfo是包含所有字段(必填 + 非必填)的实体类。

2. Mapper.xml 中的<if>用法

gender有值:

最终拼接的 SQL 是:

gendernull

最终拼接的 SQL 自动忽略gender相关片段:

3. 注解方式(不推荐)

也可以用@Insert注解 +<script>标签写动态 SQL,但可读性差,所以一般不用:

关键注意点
  1. test里的表达式是 “实体类属性”,不是数据库字段:比如test="gender != null"中的genderUserInfo类的属性名,不是数据库表的列名。

  2. 为什么不能直接写null如果不判断,直接写gender字段,当传值为null时:

    • 若数据库中gender字段有默认值(比如默认 “未知”),会被null覆盖;
    • 若字段允许null,虽然语法没问题,但不符合 “非必填则用默认值” 的业务逻辑。
  3. 列名部分的<if>判断哪个字段要插入,VALUES 值部分就必须用完全相同的<if>判断(否则会出现 “列名和值数量不匹配” 的 SQL 语法错误)。

1.2动态SQL的<trim>标签用法

(1)<trim>标签的作用

当需要动态生成多个字段 / 条件时(比如插入语句的列名、VALUES 值,或者查询的 WHERE 条件),<trim>可以通过属性自动处理 “前缀 / 后缀” 和 “多余符号”,避免手动拼接 SQL 的冗余问题。

(2)<trim>的 4 个核心属性
属性名作用
prefix给整个语句块添加一个前缀(比如给列名块加(,给 VALUES 加(
suffix给整个语句块添加一个后缀(比如给列名块加),给 VALUES 加)
prefixOverrides自动去掉语句块开头的指定符号(比如多余的AND/OR
suffixOverrides自动去掉语句块结尾的指定符号(比如多余的逗号,
(3)案例:动态插入语句的<trim>用法

以 “插入用户信息(多字段动态可选)” 为例,解析<trim>的实际作用:

1. 动态生成 “插入列名”

2.只传 age+phone(username/password 没传)

拼接后 SQL:

  • 适配 “只补全用户联系方式” 的业务场景,不用强制传用户名密码;
  • 避免了 “没传的字段被插入 NULL” 的问题。

trim+if 动态插入的核心规则

  1. 判断一致:列名<if>判断哪个字段要插入 → VALUES<if>必须用完全相同的 test 条件(字段名、非空判断都一致),保证 “列数 = 值数”;
  2. 符号统一:列名<trim>用了什么prefix/suffix(比如())→ VALUES<trim>也用完全一样的(保证 SQL 语法对称);
  3. 逗号兜底:所有<if>结尾都加,→ 统一靠trimsuffixOverrides=","去掉最后一个冗余逗号(不用手动判断 “最后一个字段要不要加逗号”)。
反例(踩坑点)

如果违背这个规则,比如:

  • 列名判断username != null,VALUES 判断userName != null(大小写不一致)→ 可能导致列名有但值无,SQL 报错;
  • 列名<trim>(),VALUES<trim>漏加 → 生成INSERT INTO user_info (username) VALUES #{username}(语法错误);
  • 手动控制逗号(比如最后一个<if>不加,)→ 字段顺序变动时,容易少逗号 / 多逗号。
拓展:trim 不止用于插入,还能用于查询

比如动态 WHERE 条件(去掉多余的 AND/OR):

  • 逻辑:prefix="WHERE"加前缀,prefixOverrides="AND | OR"去掉开头多余的 AND/OR;
  • 最终生成:SELECT * FROM user_info WHERE username = #{username} AND age = #{age}

简单说,只要是 “动态拼接 SQL 块,需要补前后缀 / 清冗余符号”,trim+if 都是最优解,核心就是 “判断对称、符号对称、冗余交给 trim 处理”。

1.3动态SQL的<where>标签用法

(1)<where>标签的作用

当需要动态组装查询条件(比如根据入参非空情况拼接 WHERE 子句)时,<where>可以自动处理两个核心问题:

  1. 自动补 WHERE 关键词:只有当子元素(比如<if>)有内容时,才会插入WHERE
  2. 自动去冗余 AND/OR:如果条件块开头是ANDOR,会自动去掉(避免 SQL 语法错误)。
(2)案例:动态查询条件的实现

以 “根据用户对象的非空属性查询” 为例,看<where>的实际效果:

原始需求
  • 入参UserInfo对象中,非 null 的属性作为查询条件;
  • 比如入参age=18、gender=1,则查询条件为WHERE age=18 AND gender=1
代码逻辑(Mapper.xml)

(3)不同场景下的最终 SQL 效果
场景 1:入参age=18、gender=1(两个条件非空)
MyBatis 解析后生成的 SQL

  • <where>的作用:
    • 子元素有内容 → 插入WHERE
    • 第一个条件开头的and→ 自动去掉;
    • 最终条件块为WHERE age=18 AND gender=1(语法正确)。
场景 2:入参只有deleteFlag=0(一个条件非空)

最终生成的 SQL

  • <where>的作用:去掉条件开头的and,插入WHERE
场景 3:入参所有属性为 null(无查询条件)
最终生成的 SQL

  • <where>的作用:子元素无内容 → 不插入WHERE(避免生成WHERE后无条件的语法错误)。
(4)<where><trim>的关系

<where>本质是<trim>的 “语法糖”,可以用<trim>手动实现相同逻辑:

<trim>有个小缺点:当子元素无内容时,会保留WHERE关键词(比如场景 3 会生成from userinfo WHERE,导致 SQL 语法错误)。

因此,<where>是动态 WHERE 条件的更优选择(自动处理空条件 + 去冗余 AND/OR)。

核心总结

<where>的价值是 “智能拼接 WHERE 条件”:

  • 有条件 → 补WHERE+ 去开头的 AND/OR;
  • 无条件 → 不补WHERE,避免语法错误;
  • 替代手动拼接,让动态查询更简洁、安全。
维度<where>标签<trim>标签
核心定位专门解决「动态 WHERE 条件」拼接问题通用型标签,解决各类 SQL 块的拼接问题(插入 / 查询 / 更新)
自动补前缀仅补WHERE(仅当子元素有内容时)可自定义前缀(如WHERE/SET/(等)
自动去冗余符号仅去掉开头AND/OR可自定义去掉「开头 / 结尾」的任意符号(如AND/,/OR等)
空条件处理子元素无内容 → 不生成WHERE(无语法错误)子元素无内容 → 仍会生成自定义前缀(如prefix="WHERE"会导致WHERE无后续条件,报错)
等价<trim>写法`<trim prefix="WHERE" prefixOverrides="ANDOR">`无固定等价写法,按需配置prefix/suffix/prefixOverrides/suffixOverrides
典型使用场景动态查询的 WHERE 条件拼接(最常用)1. 动态插入的列名 / VALUES 拼接(去结尾逗号)2. 动态更新的 SET 子句(去结尾逗号)3. 自定义复杂拼接规则
优势专为 WHERE 条件设计,无需手动配置,更简洁通用性强,适配所有动态 SQL 拼接场景
劣势功能单一,仅能处理 WHERE 条件需手动配置属性,空条件场景需额外判断
  1. 优先用<where>的场景:只要是「动态组装 WHERE 查询条件」,优先选<where>(无需配置属性,自动处理空条件 + 冗余 AND/OR,不易出错)。
  2. 必须用<trim>的场景
    • 动态插入(拼接列名 / VALUES,去结尾逗号);
    • 动态更新(拼接 SET 子句,如<trim prefix="SET" suffixOverrides=",">);
    • 需要自定义前缀 / 后缀 / 冗余符号(比如拼接 IN 条件的括号)。
  3. 避坑点:用<trim>模拟<where>时,一定要加额外判断(比如<if>兜底),避免子元素无内容时生成WHERE关键词导致 SQL 报错。

示例:<trim>模拟<where>并兜底(避免空条件)

1.4动态SQL的<set>标签用法

(1)<set>标签的作用

在动态UPDATE语句中,<set>可以自动处理两个关键问题:

  1. 自动补SET关键词:仅当子元素(比如<if>)有内容时,才插入SET
  2. 自动删冗余逗号:如果更新字段列表的结尾有多余的逗号(,),会自动去掉(避免 SQL 语法错误)。
(2)案例:动态更新用户数据

以 “根据用户对象非空属性更新数据(ID 为更新条件)” 为例,看<set>的实际效果:

需求
  • 入参UserInfo对象中,非 null 的属性作为更新字段;
  • 必须传入id(定位要更新的用户);
  • 比如入参username="newName"、age=20,则更新 SQL 为update userinfo set username='newName', age=20 where id=?
代码逻辑(Mapper.xml)

(3)不同场景下的最终 SQL 效果
场景 1:入参username="newName"、age=20(两个字段非空)

生成的 SQL

  • <set>的作用:
    • 子元素有内容 → 插入SET
    • 去掉字段列表结尾的冗余逗号(第二个字段age=20后的逗号被自动删除)。
场景 2:入参只有deleteFlag=1(一个字段非空)

生成的 SQL

  • <set>的作用:去掉delete_flag=1后的逗号,避免生成set delete_flag=1,的语法错误。
场景 3:入参只有id(无更新字段,其他属性为 null)

生成的 SQL

  • 注意:这种情况会触发 SQL 语法错误(update后无SET子句),实际开发中需要加兜底判断(比如确保至少有一个更新字段非空)。
(4)<set><trim>的关系

<set><trim>的 “语法糖”,可以用<trim>手动实现相同逻辑:

  • prefix="set"→ 补SET关键词;
  • suffixOverrides=","→ 去掉结尾的冗余逗号。

<set>是专门为UPDATE语句设计的,写法更简洁,优先推荐使用。

(5)核心总结

<set>的价值是 “智能拼接 UPDATE 的 SET 子句”:

  • 有更新字段 → 补SET+ 去结尾逗号;
  • 写法简洁,避免手动控制逗号导致的语法错误;
  • 实际开发中需加 “至少一个更新字段非空” 的判断,避免场景 3 的语法错误。

1.5动态SQL的<foreach>标签用法

(1)<foreach>的核心属性
属性名作用
collection绑定方法入参的集合 / 数组(如List/Set/ 数组,需与方法参数名一致)
item遍历过程中,集合的每个元素的别名(用于 SQL 中引用)
open遍历结果的开头拼接的字符串(如IN条件的左括号(
close遍历结果的结尾拼接的字符串(如IN条件的右括号)
separator遍历元素之间的分隔符(如IN条件的逗号,
(2)案例:批量删除用户(根据多个 ID)

以 “根据List<Integer> ids删除多个用户” 为例,解析<foreach>的效果:

接口方法

Mapper.xml 代码

(3)最终 SQL 效果

若入参ids = [1,2,3]<foreach>会动态拼接为:

  • open="("→ 开头加(
  • close=")"→ 结尾加)
  • separator=","→ 元素间加逗号;
  • item="id"→ 每个元素用#{id}引用。
(4)<foreach>的其他常见场景

除了IN条件,<foreach>还常用于批量插入

若入参是包含 2 个用户的List,生成 SQL:

(5)核心总结

<foreach>是 MyBatis 实现集合 / 数组动态拼接 SQL的核心标签,通过collection/item/open/close/separator的组合,可灵活适配IN条件、批量操作等场景,避免手动拼接集合元素的繁琐与错误。

1.6动态SQL的<include>标签的用法

(1)问题背景:SQL 片段重复的痛点

在 Mapper.xml 中,多个 SQL 语句常包含重复的片段(比如查询时的字段列表、通用条件),若直接重复编写:

  • 代码冗余,维护成本高(修改字段需改所有 SQL);
  • 易出错(不同 SQL 中字段列表不一致)。
(2)核心标签:<sql>+<include>

通过 **<sql>定义可复用片段 ** +<include>引用片段,实现 SQL 代码的复用:

标签作用核心属性
<sql>封装可复用的 SQL 片段id:片段唯一标识
<include>引用已定义的<sql>片段refid:对应<sql>id
(3)案例:复用查询字段列表

以 “多个查询 SQL 复用相同的字段列表” 为例:

1. 定义 SQL 片段(用<sql>

2. 引用 SQL 片段(用<include>

将原来重复的字段列表替换为<include>

(4)效果与优势
  1. 代码复用:重复的字段列表只需写一次,减少冗余;
  2. 维护便捷:若字段有变更(比如新增email字段),只需修改<sql id="allColumn">中的内容,所有引用该片段的 SQL 都会自动更新;
  3. 一致性:确保所有查询 SQL 的字段列表完全一致,避免漏写 / 错写字段。
(5)拓展场景

<sql>+<include>不仅可复用字段列表,还能复用通用条件(比如逻辑删除的delete_flag=0):

(6)核心总结

<include>+<sql>是 MyBatis 中SQL 代码复用的核心方案,通过 “定义片段 + 引用片段” 的方式,解决了重复 SQL 片段的冗余与维护问题,是企业级开发中提升代码可维护性的常用技巧。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/17 1:47:07

Java--双向链表

1.双向链表2.模拟实现双向链表(一).构造节点类首先我们要明白&#xff0c;双向链表的每一个节点都包含一个数据域和两个指针域&#xff0c;一个指针域为前指针域&#xff0c;表示指向当前节点的前一个节点&#xff0c;一个指针域为后指针域&#xff0c;表示指向当前节点的后一个…

作者头像 李华
网站建设 2025/12/17 1:46:21

后端springboot框架入门学习--第三篇

自动配置 可以把自动配置想象成SpringBoot 提前为你准备了大量的 “配置模板”,当你的项目引入了某个依赖、存在某个类、配置了某个属性等等时,这个模板就会自动生效,帮你完成对应的配置。 自动配置的核心:@EnableAutoConfiguration注解触发,加载并筛选XXXAutoConfigura…

作者头像 李华
网站建设 2025/12/17 1:46:07

LobeChat航班信息查询插件开发思路

LobeChat航班信息查询插件开发思路 在智能对话系统逐渐从“能说会道”走向“能干实事”的今天&#xff0c;用户不再满足于AI只是复述百科知识或生成一段文案。他们希望AI能真正帮自己完成具体任务——比如查一下航班是否延误、预订会议室、查看快递进度。这种需求催生了一个关键…

作者头像 李华
网站建设 2025/12/17 1:45:20

Python编程实战:从函数到模块化——创建自己的模块与包

免费编程软件「pythonpycharm」 链接&#xff1a;https://pan.quark.cn/s/48a86be2fdc0在Python项目中&#xff0c;当代码量超过500行时&#xff0c;将所有功能堆砌在一个文件中会变得难以维护。就像整理书房时&#xff0c;把所有书籍堆在书桌上会让人找不到需要的资料&#xf…

作者头像 李华
网站建设 2025/12/17 1:44:26

常见的软件测试用例设计方法有哪些?

常见的软件测试用例设计方法&#xff0c;个人认为主要是下面这6种&#xff1a; 1)流程图法&#xff08;也叫场景法&#xff09; 2)等价类划分法 3)边界值分析 4)判定表 5)正交法 6)错误推测法 这6种常见方法中&#xff0c;我分别按照定义、应用场景、使用步骤、案例讲解…

作者头像 李华
网站建设 2025/12/17 1:43:44

RAG技术全解析:从基础检索到智能体驱动的AI系统必学必藏

本文系统介绍了检索增强生成(RAG)技术的演进历程&#xff0c;从基础关键词检索到智能体驱动的动态推理系统。文章详细分析了RAG的核心流程、技术发展路径&#xff08;基础、进阶、模块化RAG&#xff09;&#xff0c;以及图RAG和智能体RAG的创新应用。智能体RAG通过引入具备规划…

作者头像 李华