MyBatis动态SQL详解:核心标签与性能优化指南
MyBatis动态SQL深度解析:灵活构建高效查询
动态SQL是MyBatis最强大的特性之一,它允许我们在XML映射文件中构建灵活的SQL语句,根据不同条件动态生成最终执行的SQL。本文将深入剖析MyBatis动态SQL的核心标签、OGNL表达式使用技巧以及性能优化实践。
一、核心标签与逻辑控制
1. <if>
条件判断
<if>
标签是最基础的条件判断元素,根据OGNL表达式结果决定是否包含其中的SQL片段。
<select id="findActiveBlogWithTitleLike" resultType="Blog">
SELECT * FROM blog
WHERE state = 'ACTIVE'
<if test="title != null">
AND title like #{title}
</if>
</select>
实践建议:
- 对于频繁变动的条件优先使用
<if>
- 避免在
<if>
中编写复杂逻辑,应将复杂判断放在Java代码中
2. <choose>/<when>/<otherwise>
多路选择
类似于Java中的switch-case结构,提供多条件分支选择:
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM blog WHERE state = 'ACTIVE'
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
3. <foreach>
集合遍历
处理集合参数时特别有用,常用于IN条件或批量操作:
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT * FROM post
WHERE id IN
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
参数说明:
collection
:集合参数的属性名item
:迭代时的每个元素别名index
:迭代的索引open
/close
:包裹符号separator
:元素间分隔符
批量插入示例:
<insert id="batchInsert" parameterType="java.util.List">
INSERT INTO user(name, age) VALUES
<foreach collection="list" item="user" separator=",">
(#{user.name}, #{user.age})
</foreach>
</insert>
二、智能SQL片段处理
1. <where>
智能WHERE子句
自动处理WHERE条件前的AND/OR,避免语法错误:
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM blog
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
2. <set>
智能UPDATE语句
自动处理UPDATE语句中的逗号问题:
<update id="updateAuthorIfNecessary">
UPDATE author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
WHERE id=#{id}
</update>
3. <trim>
自定义修剪
更灵活的前缀/后缀处理,可以替代<where>
和<set>
:
<!-- 等价于where标签 -->
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
<!-- 等价于set标签 -->
<trim prefix="SET" suffixOverrides=",">
...
</trim>
三、OGNL表达式详解
Object-Graph Navigation Language (OGNL) 是MyBatis动态SQL的条件判断语言,常用规则:
基本语法:
a != null
判空a or b
或运算a and b
与运算!a
非运算
集合判断:
list.size() > 0
集合非空array.length > 0
数组非空map.size() > 0
Map非空
特殊操作符:
_parameter
代表整个参数对象@class@method(args)
调用静态方法
示例:
<if test="@com.example.StringUtils@isNotEmpty(name)">
AND name = #{name}
</if>
四、动态SQL性能优化
1. 避免过度复杂的动态拼接
优化方案:
- 将复杂逻辑拆分为多个简单SQL
- 使用存储过程处理复杂业务逻辑
- 考虑使用MyBatis提供的
<sql>
片段重用
2. 批量操作优化
低效做法:
<insert id="batchInsert">
<foreach collection="list" item="item">
INSERT INTO table VALUES (#{item.val1}, #{item.val2});
</foreach>
</insert>
高效做法:
<insert id="batchInsert">
INSERT INTO table(val1, val2) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.val1}, #{item.val2})
</foreach>
</insert>
3. 索引友好性
确保动态生成的SQL能利用索引:
- 避免在索引列上使用函数或运算
- 保持查询条件顺序与索引顺序一致
- 使用
<bind>
预处理查询条件:
<select id="selectByExample">
<bind name="pattern" value="'%' + name + '%'" />
SELECT * FROM user
WHERE name LIKE #{pattern}
</select>
五、最佳实践总结
- 保持简洁:动态SQL不应过于复杂,超过5个条件考虑重构
- 测试覆盖:确保测试所有条件分支
- 性能监控:对复杂动态SQL进行性能分析
- 注释说明:为复杂逻辑添加XML注释
- 版本控制:动态SQL变更应有明确的版本记录
通过合理运用MyBatis动态SQL,可以构建出既灵活又高效的数据库访问层,适应各种复杂的业务场景需求。