MyBatis实战之动态生成SQL详解
目录
- 一、为什么需要动态SQL
- 传统SQL拼接的痛点
- 动态SQL的核心价值
- 二、动态SQL核心标签解析
- 1.<if>标签:条件分支处理
- 2.<where>标签:智能WHERE处理
- 3. 其他核心标签
- 三、最佳实践:用户查询案例
- Java调用代码
- 生成的SQL
- 参数变化时的SQL差异
- 四、高级技巧:提升动态SQL质量
- 1. 防止空字符串陷阱
- 2. 使用OGNL表达式增强判断
- 3. 复杂条件组合
- 4. 性能优化建议
- 五、企业级应用经验
- 典型应用场景
- 性能监控关键指标
- 避坑指南
在电商平台的用户管理模块中,需要面对多种不同的用户查询组合条件。当使用传统的硬编码SQL方式时,代码膨胀到了2000多行,维护成本极高。而引入MyBATis动态SQL后,同样的功能仅用300行代码实现,且可读性提升了3倍——这就是动态SQL的威力!
一、为什么需要动态SQL
传统SQL拼接的痛点
// 传统方式需要手动拼接SQL字符串
StringBuilder sql = new StringBuilder("SELECT * FROM user WHERE 1=1");
if (user.getId() != null) {
sql.append(" AND id = ").append(user.getId());
}
if (user.getUsername() != null) {
sql.append(" AND username = '").append(user.getUsername()).append("'");
}
// 存在SQL注入风险!且代码冗长难维护
动态SQL的核心价值
- 消除重复代码:相同业务逻辑不再需要重复编写
- 防止SQL注入:自动使用预编译参数
- 提升可读性:SQL与Java逻辑解耦
- 降低出错率:避免手动拼接错误
二、动态SQL核心标签解析
1.<if>标签:条件分支处理
业务场景:根据传入参数动态添加查询条件
<select id="findUsers" parameterType="User" resultType="User">
SELECT * FROM user
<where>
<if test="id != null">
AND id = #{id}
</if>
<if test="username != null and username != ''">
AND username = #{username}
</if>
<if test="password != null">
AND password = #{password}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
执行原理:

2.<where>标签:智能WHERE处理
自动处理:
- 条件前多余的AND/OR
- 当所有条件都不满足时,移除WHERE关键字
错误示例:
<!-- 当所有if都不满足时,SQL会变成:SELECT * FROM user WHERE -->
SELECT * FROM user
WHERE
<iandroidf test="id != null">id = #{id}</if>
正确用法:
SELECT * FROM user
<where>
<if test="id != null">id = #{id}</if>
<!-- 其他条件... -->
</where>
3. 其他核心标签
| 标签 | 应用场景 | 示例片段 |
|---|---|---|
| <choose> | 多选一逻辑(类似switch-case) | <when test="...">...<otherwise>... |
| <foreach> | 遍历集合(IN查询等) | item in #{ids} open="(" close=")" |
| <set> | 动态更新语句 | 更新时自动处理SET后的逗号 |
| <trim> | 自定义字符串修剪 | 可替代where/set的更灵活方案 |
三、最佳实践:用户查询案例
Java调用代码
// 创建参数对象
User queryParams = new User();
queryParams.setId(1);
queryParams.setPassword("securePass123");
// 执行动态查询
UserRepository repo = sqlSession.getMapper(UserRepository.class);
User result = repo.get(queryParams);
生成的SQL
SELECT * FROM user
WHERE
id = ?
AND password = ?
参数变化时的SQL差异
| 传入参数 | 生成SQL |
|---|---|
| 只设置id=1 | SELECT * FROM user WHERE id = ? |
| 设置username和password | SELECT ... WHERE username=? AND password=? |
| 不传任何参数 | SELECT * FROM user(无WHERE条件) |
四、高级技巧:提升动态SQL质量
1. 防止空字符串陷阱
<!-- 增加空字符串检查 --> <if test="username != null and username != ''">
2. 使用OGNL表达式增强判断
<if test="@org.apache.commons.lang3.StringUtils@isNotBlank(email)">
AND email = #{email}
</if>
3. 复杂条件组合
<select id="searchUsers">
SELECT * FROM users
<where>
<choose>
<when test="status != null">
kSgahndT status = #{status}
</when>
<otherwise>
status = 'ACTIVE'
</otherwise>
</choose>
<if test="name != null">
AND (first_name LIKE #{name} OR last_name LIKE #{name})
</if>
</where>
ORDER BY
<foreach item="item" collection="sortBy" separator=",">
${item}
</foreach>
</select>
4. 性能优化建议
<!-- 使用<bind>预先处理值 -->
<bind name="pattern" value="'%' + name + '%'" />
<if test="name != null">
AND username LIKE #{pattern}
</if>
五、企业级应用经验
典型应用场景
- 动态报表系统:根据前端选择的50+字段生成查询
- 高级搜索功能:支持多条件组合筛选
- 批量操作:使用foreach处理集合参数
- 多租户系统:动态添加租户ID条件
性能监控关键指标
// 输出动态SQL configuration.setLogImpl(StdOutImpl.class); // 监控结果 DEBUG [main] - ==> phpPreparing: SELECT * FROM user WHERE id = ? DEBUG [main] - ==> Parameters: 1(Integer)
避坑指南
- 避免过度动态化:超过10个条件的查询建议拆分
- 注意空格处理:标签内换行可能导致SQL语法错误
- 慎用${}:优先使用#{}防止SQL注入
- 单元测试覆盖:至少覆盖边界条件组合
在物流系统中应用动态SQL的真实数据:运单查询接口响应时间从120ms降至45ms,代码维护成本降低70%。但切记:动态SQL是利器而非银弹——kSgahndT当逻辑复杂到难以维护时,请考虑改用存储过程或Elasticsearch等专业方案!
思考题:当动态SQL生成的查询性能突然下降,你会如何诊断问题?欢迎分享你的排查思路!
到此这篇关于MyBatis实战之动态生成SQL详解的文章就介绍到这了,更多相关MyBatis动态SQL内容请搜索编程客栈(www.cppcns编程.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
加载中,请稍侯......
精彩评论