实用指南:MyBatis 的动态 SQL
1. 动态 SQL 的定义
动态 SQL 是 MyBatis 的核心特性之一,它允许开发者根据运行时条件动态生成 SQL 语句。通过特殊的 XML 标签或注解语法,实现 SQL 的灵活拼接,避免在 Java 代码中手动拼接 SQL 字符串的复杂性和安全风险。
2. 核心作用
- 条件分支:根据参数值动态包含/排除 SQL 片段(如 WHERE 条件过滤)。 
- 循环处理:遍历集合生成批量操作(如 IN 查询、批量插入)。 
- 智能语法处理:自动去除多余的 - AND/- OR或逗号,保证 SQL 语法正确性。
- 防止 SQL 注入:通过参数占位符( - #{})预编译 SQL。
3. 常用标签详解
| 标签 | 作用 | 示例 | 
|---|---|---|
| <if> | 条件判断,满足条件时包含 SQL 片段 | xml <if test="name != null">AND name = #{name}</if> | 
| <choose> | 多条件分支(类似 Java 的 switch-case) | xml <choose><when test="age != null">AND age = #{age}</when><otherwise>AND age > 18</otherwise></choose> | 
| <foreach> | 遍历集合(如 List、Array) | xml <foreach item="id" collection="ids" open="(" separator="," close=")">#{id}</foreach> | 
| <where> | 智能处理 WHERE 子句,自动去除开头的 AND/OR | xml <where><if test="name != null">AND name = #{name}</if></where> | 
| <set> | 智能处理 UPDATE 语句,自动去除结尾逗号 | xml <set><if test="name != null">name = #{name},</if></set> | 
| <trim> | 自定义字符串修剪(灵活处理前后缀) | xml <trim prefix="WHERE" prefixOverrides="AND">...</trim> | 
| <bind> | 创建变量并绑定到上下文(用于模糊查询等) | xml <bind name="pattern" value="'%' + keyword + '%'" /> | 
4. 典型应用场景
- 多条件组合查询 - SELECT * FROM user AND name LIKE #{name} AND age >= #{minAge} AND age- 2.批量插入数据 - INSERT INTO user (name, age) VALUES (#{user.name}, #{user.age})- 3.动态更新字段 - UPDATE user name = #{name}, age = #{age}, WHERE id = #{id}
5. 优势分析
| 优势 | 说明 | 
|---|---|
| 代码简洁 | 避免 Java 代码中复杂的 StringBuilder拼接 | 
| 可维护性强 | SQL 与 Java 代码解耦,修改 SQL 无需重新编译 Java | 
| 安全性高 | 自动使用 PreparedStatement预编译,防止 SQL 注入 | 
| 智能语法处理 | 自动处理逗号、AND/OR 等语法细节 | 
| 灵活性高 | 支持复杂逻辑(嵌套条件、循环等) | 
6. 底层实现原理
MyBatis 动态 SQL 基于 OGNL(Object-Graph Navigation Language)表达式 实现:
- 解析 XML 标签时,根据 - test属性中的 OGNL 表达式计算布尔值。
- 遍历 SQL 节点树,动态拼接符合条件的 SQL 片段。 
- 最终生成标准的 - PreparedStatement可执行的 SQL。
7. 最佳实践
- 避免过度复杂:动态 SQL 嵌套不超过 3 层,否则可拆分为多个查询。 
- 性能优化:使用 - <where>、- <set>减少无效字符处理开销。
- 参数校验:在 Java 层校验参数合法性,减少动态 SQL 判断分支。 
- 注解替代 XML:简单场景可用 - @SelectProvider注解实现动态 SQL:- @SelectProvider(type = UserSqlBuilder.class, method = "buildQuery")List findUsers(UserQuery query); class UserSqlBuilder { public String buildQuery(UserQuery query) { return new SQL() {{ SELECT("*"); FROM("user"); if (query.getName() != null) WHERE("name = #{name}"); }}.toString(); }}- 8. 对比其他方案- 方案 - 动态 SQL - Java 拼接 SQL - 可读性 - 高(结构化标签) - 低(大量字符串拼接) - 安全性 - 高(自动预编译) - 低(易引发 SQL 注入) - 维护成本 - 低(SQL 独立管理) - 高(SQL 散落在 Java 代码中) - 复杂逻辑支持 - 强(标签嵌套) - 弱(需手动处理语法细节) 
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号