Spring学习笔记【8】
MyBatis 动态SQL
📖 目录
- 动态SQL概述
- if元素 - 条件判断
- choose/when/otherwise元素 - 分支选择
- trim元素 - 前后缀处理
- where元素 - 智能条件处理
- set元素 - 动态更新
- foreach元素 - 集合遍历
- bind元素 - 变量绑定
- 总结
🔍动态SQL概述
动态SQL是MyBatis的核心特性之一,它能够根据不同条件动态生成SQL语句,解决了传统静态SQL在复杂业务场景下的局限性。
为什么需要动态SQL? 在实际开发中,我们经常遇到这样的场景:
- 根据用户输入的不同条件进行查询
- 批量操作时参数数量不确定
- 更新操作时只更新有值的字段
- 不同业务逻辑需要不同的SQL片段
动态SQL通过各种标签元素,让我们能够在XML映射文件中编写灵活的SQL逻辑,提高代码的复用性和可维护性。
了解动态SQL的重要性后,让我们从最基础的条件判断开始学习。
🔍if元素 - 条件判断
<if>
元素是动态SQL中最基础也是最常用的元素,用于根据条件决定是否包含某个SQL片段。
2.1 基本用法
<!-- 使用if元素,根据条件动态查询用户信息 -->
<select id="selectUserByIf" resultType="com.po.MyUser" parameterType="com.po.MyUser">select * from user where 1=1 <if test="uname != null and uname != ''">and uname like concat('%', #{uname}, '%')</if><if test="usex != null and usex != ''">and usex = #{usex}</if>
</select>
2.2 test属性详解
test
属性中可以使用的表达式:
- 空值判断:
uname != null
或uname == null
- 字符串判断:
uname != ''
或uname == ''
- 数值比较:
age > 18
或score >= 60
- 逻辑运算:
and
、or
、not
注意事项:
- 使用
1=1
作为基础条件,避免第一个条件前的and
造成语法错误 - 字符串类型建议同时判断
!= null
和!= ''
- 数值类型只需判断
!= null
🔍choose/when/otherwise元素 - 分支选择
当需要在多个条件中选择一个执行时,使用 <choose>
元素,类似于Java中的 switch-case
语句。
3.1 基本结构
<!-- 使用choose、when、otherwise元素,根据条件动态查询用户信息 -->
<select id="selectUserByChoose" resultType="com.po.MyUser" parameterType="com.po.MyUser">select * from user where 1=1 <choose><when test="uname != null and uname != ''">and uname like concat('%', #{uname}, '%')</when><when test="usex != null and usex != ''">and usex = #{usex}</when><otherwise>and uid > 10</otherwise></choose>
</select>
3.2 执行逻辑
<choose>
相当于switch
<when>
相当于case
,按顺序检查条件<otherwise>
相当于default
,当所有when
条件都不满足时执行
使用场景:
- 多个互斥条件中选择一个
- 设置默认查询条件
- 根据不同条件使用不同的排序方式
🔍trim元素 - 前后缀处理
<trim>
元素提供了强大的前后缀处理能力,是处理动态SQL中最灵活的元素。
4.1 属性说明
<!-- 使用trim元素,根据条件动态查询用户信息 -->
<select id="selectUserByTrim" resultType="com.po.MyUser" parameterType="com.po.MyUser">select * from user <trim prefix="where" prefixOverrides="and |or "><if test="uname != null and uname != ''"> and uname like concat('%', #{uname}, '%')</if><if test="usex != null and usex != ''"> and usex = #{usex} </if></trim>
</select>
属性详解:
prefix
:在内容前添加指定前缀(如 “where”)suffix
:在内容后添加指定后缀prefixOverrides
:去除内容开头的指定字符(如 "and "、"or ")suffixOverrides
:去除内容结尾的指定字符(如 “,”)
4.2 应用场景
<!-- 动态插入语句 -->
<insert id="insertUser" parameterType="com.po.MyUser">insert into user<trim prefix="(" suffix=")" suffixOverrides=","><if test="uname != null">uname,</if><if test="usex != null">usex,</if><if test="uage != null">uage,</if></trim>values<trim prefix="(" suffix=")" suffixOverrides=","><if test="uname != null">#{uname},</if><if test="usex != null">#{usex},</if><if test="uage != null">#{uage},</if></trim>
</insert>
🔍where元素 - 智能条件处理
<where>
元素是 <trim>
的简化版本,专门用于处理查询条件。
5.1 基本用法
<!-- 使用where元素,根据条件动态查询用户信息 -->
<select id="selectUserByWhere" resultType="com.po.MyUser" parameterType="com.po.MyUser">select * from user <where><if test="uname != null and uname != ''">and uname like concat('%', #{uname}, '%')</if><if test="usex != null and usex != ''">and usex = #{usex}</if></where>
</select>
5.2 智能处理
<where>
元素会智能地处理以下情况:
- 当包含内容为空时,不会生成
where
关键字 - 自动去除开头的
and
或or
- 只有当包含内容不为空时才生成
where
子句
等价的trim写法:
<trim prefix="where" prefixOverrides="and |or "><!-- 内容 -->
</trim>
🔍set元素 - 动态更新
<set>
元素专门用于动态更新操作,能够智能处理更新字段。
6.1 基本用法
<!-- 使用set元素,动态修改一个用户 -->
<update id="updateUserBySet" parameterType="com.po.MyUser">update user <set><if test="uname != null">uname = #{uname},</if><if test="usex != null">usex = #{usex},</if><if test="uage != null">uage = #{uage},</if></set>where uid = #{uid}
</update>
6.2 智能处理特性
- 自动添加
SET
关键字 - 自动去除最后一个多余的逗号
- 防止生成空的
SET
子句
🔍foreach元素 - 集合遍历
<foreach>
元素用于遍历集合,常用于 IN
查询、批量插入等场景。
7.1 基本用法
<!-- 使用foreach元素,查询用户信息 -->
<select id="selectUserByForeach" resultType="com.po.MyUser" parameterType="List">select * from user where uid in <foreach item="item" index="index" collection="list" open="(" separator="," close=")">#{item}</foreach>
</select>
7.2 属性详解
属性 | 说明 | 示例值 |
---|---|---|
collection | 指定要遍历的集合类型 | list/array/map |
item | 集合中每个元素的变量名 | item/user |
index | 当前元素的索引位置 | index/i |
open | 遍历开始时的拼接字符 | ( |
close | 遍历结束时的拼接字符 | ) |
separator | 元素之间的分隔符 | , |
7.3 collection属性规则
collection的值取决于参数类型
- 单个List参数:
collection="list"
- 单个Array参数:
collection="array"
- Map参数:
collection="map中的key"
- 对象属性:
collection="对象.属性名"
🔍bind元素 - 变量绑定
<bind>
元素用于创建变量并绑定到上下文中,主要解决模糊查询中的SQL注入问题和数据库兼容性问题。
8.1 解决的问题
传统方式的问题:
- 使用
${}
拼接存在SQL注入风险 - 不同数据库的字符串拼接函数不同(MySQL的
concat
、Oracle的||
)
8.2 基本用法
<!-- 使用bind元素进行模糊查询 -->
<select id="selectUserByBind" resultType="com.po.MyUser" parameterType="com.po.MyUser"><!-- bind中uname是com.po.MyUser的属性名,paran_uname是自定义变量名 --><bind name="paran_uname" value="'%' + uname + '%'"/>select * from user where uname like #{paran_uname}
</select>
8.3 表达式语法
<bind>
的 value
属性支持:
- 字符串拼接:
'%' + name + '%'
- 条件表达式:
age > 18 ? '成年' : '未成年'
- 对象属性访问:
user.name + '(' + user.id + ')'
- 方法调用:
name.toLowerCase()
🔍总结
MyBatis动态SQL通过不同的标签元素实现了灵活的SQL生成:
元素 | 作用 | 适用场景 | 核心特性 |
---|---|---|---|
<if> | 条件判断 | 根据条件包含SQL片段 | 简单条件判断 |
<choose> | 分支选择 | 多个条件中选择一个 | 类似switch-case |
<trim> | 前后缀处理 | 灵活的前后缀管理 | 高度可定制化 |
<where> | 条件子句 | 动态生成WHERE条件 | 智能处理and/or |
<set> | 更新子句 | 动态UPDATE操作 | 智能处理逗号 |
<foreach> | 集合遍历 | 批量操作、IN查询 | 支持多种集合类型 |
<bind> | 变量绑定 | 模糊查询、变量处理 | 防SQL注入,跨数据库兼容 |
最后希望这份笔记能给你带来微薄的帮助!🎉