动态SQL
动态SQL是MyBatis最强大的特性之一,它允许你根据条件动态拼接SQL语句,解决传统JDBC中大量拼接字符串的繁琐问题。
1. 什么是动态SQL?
动态SQL是一种根据运行时条件动态生成和执行SQL语句的技术。与静态SQL(在代码中固定写死的SQL)不同,动态SQL允许程序根据不同输入或条件动态灵活构建SQL,适用于需要灵活查询、参数化条件或复杂业务逻辑的场景。
- 核心特点:通过条件判断、循环等逻辑,灵活拼接SQL字段,避免硬编码复杂的查询或更新逻辑。
- 典型场景:多条件查询、批量操作、动态字段更新等。
2. 动态SQL的作用
- 灵活适配业务需求:根据参数动态生成不同的SQL,避免写多个重复的静态SQL。
- 减少代码冗余:通过逻辑控制标签(如if、foreach)简化代码。
- 提升可维护性:集中管理SQL逻辑,修改时只需调整XML或模板文件。
- 防止SQL注入:结合参数化查询(#{ }),避免直接拼接字符串的风险。
3. 动态SQL的常用标签
1. <if>标签:条件判断
作用:根据表达式的值(true/false)决定是否包含标签内的内容。
示例:根据传入参数动态更新字段。
<update id="updateUser">UPDATE userSET<if test="name != null">name = #{name},</if><if test="age != null">age = #{age},</if><if test="email != null">email = #{email}</if>WHERE id = #{id}
</update>
风险点:如果最后一个<if>条件不满足,SQL末尾会多一个逗号,导致语法错误!
错误SQL:
UPDATE user SET name = 'John', WHERE id = 1; -- 逗号多余
2. <set>标签:动态更新字段(自动处理逗号)
作用:专用于处理UPDATE语句,自动去除SET子句末尾多余的逗号。
修正后的安全写法:
<update id="updateUser">UPDATE user<set><if test="name != null">name = #{name},</if><if test="age != null">age = #{age},</if><if test="email != null">email = #{email},</if></set>WHERE id = #{id}
</update>
MyBatis会智能生成:
-- 当只有 name 和 age 有值时
UPDATE user SET name = 'John', age = 25 WHERE id = 1;
3. <trim>标签:自定义字符串处理(自定义修剪SQL片段的前缀、后缀)
作用:更灵活地处理前后缀,可替代<set>或<where>。
示例:用<trim>实现<set>的效果:
<trim prefix="SET" suffixOverrides=","><if test="name != null">name = #{name},</if><if test="age != null">age = #{age},</if>
</trim>
等同于<set>,但可以自定义前缀和要删除的后缀。
- 属性及作用:
- prefix:添加前缀
- suffix:添加后缀
- prefixOverride:去除开头匹配的字符串
- suffixOverride:去除结尾匹配的字符串
4. <where>标签:智能处理where子句
作用:自动去除WHERE后的首个AND或OR,避免语法错误。
错误场景:
<select id="findUser">SELECT * FROM userWHERE<if test="name != null">AND name = #{name}</if><if test="age != null">AND age = #{age}</if>
</select>
若所有条件都不满足,生成的SQL是:SELECT * FROM user WHERE (语法错误)。
修正后:
<select id="findUser">SELECT * FROM user<where><if test="name != null">AND name = #{name}</if><if test="age != null">AND age = #{age}</if></where>
</select>
MyBatis会处理成:SELECT * FROM user WHERE name = 'John' 或直接去掉WHERE。
5. <foreach>标签:遍历集合或数组
作用:常用于IN查询或批量插入。
5.1 批量新增(List集合)
示例:根据id列表查询用户:
<insert id="batchInsert">INSERT INTO user (name, age) VALUES<foreach collection="list" item="user" separator=",">(#{user.name}, #{user.age})</foreach>
</insert>
生成SQL:select * from user where id in(1,2,3)
- 参数说明:
- collection="list":集合参数名(若参数是List类型,默认名为list)
- item:遍历时的元素别名
- separator:元素间的分隔符
5.2 批量删除(数组)
<delete id="batchDelete">DELETE FROM user WHERE id IN<foreach collection="array" item="id" open="(" separator="," close=")">#{id}</foreach>
</delete>
- 参数说明:
- collection="array":数组参数默认名为array。
- open/close:包裹遍历结果的前后缀。
6. <choose>标签:
作用:实现多条件分支逻辑(类似switch-case)
<select id="findUser" resultType="User">SELECT * FROM user<where><choose><when test="name != null">name = #{name}</when><when test="email != null">email = #{email}</when><otherwise>status = 'ACTIVE'</otherwise></choose></where>
</select>