"小崔,动态SQL 不是10 个标签背一遍,是4 套积木组合。记住场景,比记住标签重要。"——白歌
本章定位
一句话:本章解决"如何根据运行时条件动态组装SQL、避免拼接字符串带来的SQL 注入和代码混乱"的问题。
飞翔科技的后台管理系统上线后,黄俪设计的筛选面板有12 个可选条件,小崔在DAO 层用Java 拼接SQL,不仅代码臃肿,还差点引入SQL 注入漏洞。白歌直接否决:全部改用MyBatis 动态SQL 标签。这一章,小崔把10 个标签当成4 套"积木"来学。
学习路线图
学习顺序说明:
- 先学条件判断(if / choose_when_otherwise)→ 掌握"要不要加这段SQL"的控制权
- 再学智能包裹(where / set / trim)→ 掌握"加了之后前缀后缀怎么处理"的自动化
- 再学批量迭代(foreach / bind)→ 掌握"IN 查询、批量插入、动态变量绑定"
- 最后看高级扩展(script / databaseId / 脚本语言)→ 掌握多数据库适配和OGNL/MVEL 等脚本扩展
文件关系说明
| 文件 | 一句话角色 |
|---|---|
if.md | 条件判断基石:单条件开关,test 属性写OGNL 表达式,决定是否包含某段SQL |
choose_when_otherwise.md | 多选一组合:Java 中switch 的XML 等价物,互斥条件场景(如:按状态/按时间/默认) |
where.md | 智能WHERE 包裹:自动处理首条条件前的AND/OR,避免WHERE AND语法错误 |
trim.md | 通用前缀后缀裁剪:where 和set 的"超集",可自定义prefix/suffix/prefixOverrides/suffixOverrides |
set.md | 智能SET 包裹:动态更新时自动处理字段间的逗号,避免SET col1=?, ,col2=?错误 |
foreach.md | 批量迭代核心:IN 查询、批量插入、批量更新的循环构造器 |
bind.md | 变量预绑定:在SQL 执行前创建OGNL 变量,常用于模糊查询拼接% |
script.md | 脚本包裹器:在注解或XML 中包裹动态SQL 脚本,支持更复杂的逻辑 |
_databaseId 与动态SQL 多数据库支持.md | 多数据库适配:根据databaseId 切换不同方言的动态SQL 分支 |
动态SQL 中插入脚本语言.md | 语言扩展:在动态SQL 中嵌入OGNL、MVEL 等脚本引擎的进阶用法 |
知识图谱
核心概念关系:
if和choose是"决策层",决定哪些SQL 片段进入最终语句where / set / trim是"格式化层",解决动态拼接带来的语法瑕疵(多余的AND、逗号)foreach和bind是"数据层",处理集合遍历和变量预处理- 常见组合场景:
- if + where:多条件查询的标准组合,where 自动吞掉第一个if 前的AND
- foreach + bind:批量IN 查询前用bind 拼接
%keyword%,再用foreach 遍历ID 列表 - choose + when + otherwise:排序规则互斥选择(按时间/按热度/默认按ID)
- trim + set:复杂更新场景,set 是trim 的特例,trim 可处理更自定义的前缀后缀
与下一章的衔接
本章学完后,小崔能写灵活的动态SQL,但遇到"订单里嵌套用户信息"、"部门下挂多个员工"这种对象关系映射时,单表的resultType 和简单resultMap 已经不够用了。
下一章《结果映射与关联查询》将解决:如何用 association(一对一)、collection(一对多)、discriminator(鉴别器)处理复杂对象图,以及延迟加载和N+1 问题的规避策略。
"动态SQL 解决了'SQL 怎么写'的问题,结果映射解决了'查回来的数据怎么变成对象'的问题。两个都通了,才算真正会写MyBatis。"——白歌