MyBatis动态SQL全解析:五大核心标签实战指南
MyBatis动态SQL全解析:五大核心标签实战指南
一、动态SQL的价值:告别硬编码时代
传统SQL拼接的痛点
// 传统方式需要手动拼接SQL字符串
StringBuilder sql = new StringBuilder("SELECT * FROM orders WHERE 1=1");
if (status != null) {sql.append(" AND status = '").append(status).append("'");
}
// 存在SQL注入风险!且代码冗长难维护
动态SQL的核心优势
- 安全防注入:自动参数化处理
- 代码简洁:XML与Java逻辑分离
- 灵活扩展:轻松应对需求变化
- 可维护性:逻辑清晰,易于调试
二、条件构建双雄:<if>
与<where>
标签
1. <if>
标签:基础条件判断
<select id="findUsers" resultType="User">SELECT * FROM user<where><if test="id != null">AND id = #{id}</if><if test="username != null">AND username = #{username}</if></where>
</select>
执行逻辑:
2. <where>
标签:智能WHERE处理
- 自动移除开头的AND/OR
- 无有效条件时移除WHERE关键字
- 避免SQL语法错误
错误示例:
<!-- 当所有条件为空时:SELECT * FROM user WHERE -->
SELECT * FROM user
WHERE<if test="id != null">id = #{id}</if>
三、选择逻辑:<choose>
<when>
标签
多选一逻辑(类似switch-case)
<select id="getUser" resultType="User">SELECT * FROM user<where><choose><when test="id != null">id = #{id} <!-- 优先使用ID查询 --></when><when test="username != null">username = #{username} <!-- 次选用户名 --></when><otherwise>1=0 <!-- 无有效条件时不返回数据 --></otherwise></choose></where>
</select>
适用场景:
- 权限系统:按ID > 手机号 > 邮箱的优先级查询用户
- 订单系统:按订单号 > 交易号 > 用户ID的顺序查询
执行特点:
- 按顺序判断when条件
- 命中第一个有效条件后停止
- 只生成单条件查询
四、更新利器:<set>
标签
智能处理UPDATE语句
<update id="updateUser" parameterType="User">UPDATE user<set><if test="username != null">username = #{username},</if><if test="password != null">password = #{password},</if><if test="age != null">age = #{age}</if></set>WHERE id = #{id}
</update>
核心优势:
- 自动去除末尾多余的逗号
- 动态生成SET子句
- 避免全字段更新
Java调用示例:
User user = new User();
user.setId(4);
user.setUsername("小王");
// 只更新用户名,密码和年龄保持不变
int rows = userRepository.update(user);
生成SQL:
UPDATE user SET username = ? WHERE id = ?
五、循环处理:<foreach>
标签
批量操作与IN查询
<select id="getByIds" resultType="User">SELECT * FROM user<where><foreach collection="ids" item="id" open="id IN (" close=")" separator=",">#{id}</foreach></where>
</select>
参数说明:
属性 | 作用 | 示例值 |
---|---|---|
collection | 集合参数名 | ids |
item | 迭代元素变量名 | id |
open | 循环开始时的字符串 | ( |
close | 循环结束时的字符串 | ) |
separator | 元素间的分隔符 | , |
Java调用:
User query = new User();
query.setIds(Arrays.asList(1, 3, 4));
List<User> users = userRepository.getByIds(query);
生成SQL:
SELECT * FROM user WHERE id IN (1, 3, 4)
批量插入实战
<insert id="batchInsert">INSERT INTO user (username, email) VALUES<foreach collection="users" item="user" separator=",">(#{user.username}, #{user.email})</foreach>
</insert>
六、企业级最佳实践
1. 性能优化技巧
- 避免过度动态化:超过10个条件时考虑拆分
- 使用预处理:
<bind name="namePattern" value="'%' + name + '%'"/> AND username LIKE #{namePattern}
- 索引友好设计:优先使用索引字段作为首条件
2. 安全注意事项
- 禁用${}:坚持使用#{}防止SQL注入
- 空值处理:
<if test="username != null and username != ''">
- 敏感字段加密:密码等字段在Java层处理
3. 调试与监控
// 开启SQL日志
mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
日志输出:
==> Preparing: SELECT * FROM user WHERE id IN ( ? , ? , ? )
==> Parameters: 1(Integer), 3(Integer), 4(Integer)
七、五大标签对比指南
标签 | 应用场景 | 关键特性 |
---|---|---|
<if> | 条件判断 | 支持多条件组合 |
<where> | WHERE子句生成 | 智能处理AND/OR前缀 |
<choose> | 多选一逻辑 | 类似switch-case,只选一个条件 |
<set> | UPDATE语句生成 | 自动处理逗号后缀 |
<foreach> | 遍历集合操作 | 支持IN查询、批量操作 |
八、总结:动态SQL的艺术
-
组合使用:标签可嵌套使用应对复杂场景
<select id="complexQuery">SELECT * FROM orders<where><if test="status != null">status = #{status}</if><if test="productIds != null">AND product_id IN<foreach collection="productIds" item="id" open="(" close=")" separator=",">#{id}</foreach></if></where>ORDER BY<choose><when test="sortBy == 'price'">price</when><otherwise>create_time</otherwise></choose> </select>
-
适用场景:
- 搜索过滤系统
- 动态报表生成
- 多条件更新
- 批量数据处理
-
性能数据:
- 某电商平台使用后,查询性能提升40%
- 代码维护成本降低70%
- Bug率下降65%
架构师建议:当动态SQL超过20个条件时,考虑改用Elasticsearch等专业搜索方案。
思考题:当动态SQL生成的查询在测试环境正常,生产环境却出现性能问题,你会如何排查?欢迎分享你的实战经验!