MyBatis 动态 SQL 详解与实战
动态 SQL 是 MyBatis 的核心特性之一,它允许我们根据不同条件动态构建 SQL 语句,有效解决了传统 SQL 拼接带来的语法错误(如多余的 AND、逗号等问题)。深入解析动态 SQL 标签的使用场景、最佳实践及高级技巧。
动态 SQL 核心标签详解
1. if 标签:条件判断
if 标签是动态 SQL 中最基础的条件判断标签,通过 test 属性(OGNL 表达式)决定是否拼接其内部的 SQL 片段。
基础用法
1 | <select id="findUsers" resultType="User"> |
注意事项
test属性中使用 OGNL 表达式,判断字符串非空需同时检查!= null和!= '';- 数值类型只需判断
!= null(避免!= 0误判合法的 0 值); - 多条件判断可用
&&(需转义为&&)或and连接。
2. where 标签:智能处理条件前缀
where 标签解决了 if 标签可能导致的「多余 AND/OR」问题,它会自动去除条件片段开头的 AND 或 OR,并在有条件时添加 WHERE 关键字。
替代 1=1 的优雅方案
1 | <select id="findUsers" resultType="User"> |
工作原理
- 若内部条件均不满足,
where标签不会生成任何内容; - 若条件片段以
AND或OR开头,会自动剔除; - 若条件片段正常,会在开头添加
WHERE。
3. trim 标签:自定义字符串处理
trim 标签是更灵活的条件拼接工具,通过 prefix、suffix、prefixOverrides、suffixOverrides 四个属性自定义拼接规则,可替代 where 和 set 标签。
属性说明
| 属性 | 作用 |
|---|---|
prefix |
给拼接后的整个字符串添加前缀 |
suffix |
给拼接后的整个字符串添加后缀 |
prefixOverrides |
去除拼接字符串开头的指定字符 |
suffixOverrides |
去除拼接字符串结尾的指定字符 |
替代 where 标签
1 | <trim prefix="WHERE" prefixOverrides="AND | OR"> |
替代 set 标签(更新场景)
1 | <trim prefix="SET" suffixOverrides=","> |
4. set 标签:动态更新字段
set 标签用于 UPDATE 语句,自动添加 SET 关键字,并去除字段末尾多余的逗号。
示例:只更新非空字段
1 | <update id="updateUser"> |
5. choose-when-otherwise:分支选择
类似 Java 的 switch-case,只执行第一个满足条件的 when 标签,若所有 when 都不满足,则执行 otherwise 标签(可选)。
示例:优先按 ID 查询,其次按用户名,否则查所有
1 | <select id="findUser" resultType="User"> |
6. foreach 标签:遍历集合
foreach 标签用于遍历数组或集合,常用于 IN 条件、批量插入、批量更新等场景。
核心属性
| 属性 | 作用 | 常用值 |
|---|---|---|
collection |
需遍历的集合 / 数组名称 | list(List)、array(数组)、map的key |
item |
遍历元素的别名 | 自定义变量名(如 item、user) |
separator |
元素之间的分隔符 | ,、OR 等 |
open |
拼接结果的开头字符 | ( |
close |
拼接结果的结尾字符 | ) |
index |
遍历索引(List 为索引,Map 为 key) | 自定义变量名(如 index) |
场景 1:IN 条件查询
1 | <select id="findUsersByIds" resultType="User"> |
场景 2:批量插入
1 | <insert id="batchInsertUsers"> |
场景 3:批量更新(MySQL)
1 | <update id="batchUpdateStatus"> |
7. 其他实用标签
sql 与 include:SQL 片段复用
sql 标签用于抽取重复的 SQL 片段(如查询字段、表名),include 标签用于引用,提升代码复用性。
1 | <!-- 定义SQL片段 --> |
bind:变量绑定
bind 标签可将 OGNL 表达式的结果绑定到变量,避免不同数据库的字符串拼接差异(如 MySQL 的 CONCAT 与 Oracle 的 ||)。
1 | <select id="findUserByLikeName" resultType="User"> |
动态 SQL 高级技巧
1. 内置参数的使用
MyBatis 提供两个内置参数,可直接在动态 SQL 中使用:
_parameter:代表整个参数(单个参数时为参数本身,多个参数时为封装的 Map);_databaseId:若配置了databaseIdProvider,代表当前数据库的别名(用于多数据库适配)。
示例:使用 _parameter 简化多参数判断
1 | <select id="findUser" resultType="User"> |
示例:使用 _databaseId 适配多数据库
1 | <select id="findAll" resultType="User"> |
2. 多参数传递与动态 SQL
当方法有多个参数时,需通过 @Param 注解指定参数名,才能在动态 SQL 中正确引用:
1 | // Mapper接口 |
1 | <!-- 映射文件 --> |
3. 动态 SQL 与枚举类型
处理枚举类型时,可在 test 表达式中直接使用枚举的属性或方法:
1 | // 枚举类 |
1 | <select id="findByStatus" resultType="User"> |
常见问题与解决方案
1. foreach 标签 collection 属性值错误
问题:遍历集合时提示
Parameter 'list' not found;原因:未使用
@Param注解时,MyBatis 对 List 参数默认封装为list,数组为array,但参数名可能被覆盖;解决方案:
1
2// 明确指定参数名
List<User> findByIds( List<Long> ids);
1 | <!-- collection 与 @Param 一致 --> |
2. test 表达式中字符串判断问题
问题:
test="status == 'ACTIVE'"不生效;原因:OGNL 中单引号包裹的是字符(char),字符串需用双引号或转义单引号;
解决方案:
1 | <if test='status == "ACTIVE"'>...</if> <!-- 推荐:外单内双 --> |
3. 批量插入性能优化
问题:
foreach批量插入大量数据时性能差;解决方案:
控制批次大小(如每 1000 条一批);
开启批量执行模式(rewriteBatchedStatements=true):
1
=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
最佳实践总结
- 优先使用
where和set标签:替代trim处理常见场景,简化代码; - 避免
1=1拼接:where标签已完美解决条件为空问题; foreach批量操作分批处理:防止 SQL 过长导致性能问题;- 抽取 SQL 片段:通过
sql和include复用重复代码; - 多数据库适配:结合
_databaseId和choose标签兼容不同数据库语法; - 参数命名规范:多参数时必用
@Param注解,提高可读性
v1.3.10