Mybatis
一、Mybatis 参数处理
Mybatis 所有参数传递底层是 Map 集合
-
四种核心参数传递方式
-
多个基本类型参数(无封装)
-
用法:接口方法直接声明多个基本类型
// Mapper接口
User login(String username, String password);
-
取值方式:XML 中用 arg0,arg1...(按顺序从 0 开始)或 param1,param2...(按顺序从 1 开始)
<select id="login" resultType="cn.wolfcode.user.domain.User">
select * from user where username=#{param1} and password=#{param2}
</select>
-
缺点:参数多时,容易混淆
-
@Param注解封装 -
用法:通过
@Param注解指定 Map 的 key ,直接关联 XML 中的取值名
// Mapper接口:给参数指定key为username和password
User login(@Param("username") String username, @Param("password") String password);
-
取值方法:XML 直接用注解指定的 key (#{username}、#{password})
-
底层原理:自动将参数封装为 Map ,key = 注解值,value = 参数值
-
Map 手动封装
-
用法:手动创建 HashMap ,存入参数键值对,接口方法接收 Map 对象
// Mapper接口
User login(Map<String, Object> map);
// 测试代码
@Test
public void login() {
Map<String, Object> map = new HashMap<>();
map.put("username", "admin"); // key=username
map.put("password", "222"); // key=password
userMapper.login(map);
}
-
取值方式:XML 直接用 Map 的 key(与上面一致)
-
适用场景:参数无固定实体类(如多条件筛选的零散参数)
-
JavaBean 封装
-
用法:将参数封装为 JavaBean (如 User、Employee),接口方法接收该对象
// Mapper接口
User login(User u);
// 测试代码
@Test
public void login() {
User u = new User();
u.setUsername("admin");
u.setPassword("222");
userMapper.login(u);
}
-
取值方式:XML 直接用 JavaBean 的属性名
-
适用场景:参数与实体类字段对应(如新增,修改,多条件查询)
-
特殊参数:List/ 数组
-
自动封装规则:Mybatis 会自动将其存入 Map ,无需手动处理
-
List 集合: Map 的key 固定为 list
-
数组:Map 的 key 固定 array
-
自定义 key :用
@Param注解修改 key 值
// 接口:将List的key改为"ids",替代默认的"list"
void batchDelete(@Param("ids") List<Long> ids);
-
取值方式:XML 中通过
collection="list"(默认)或collection="ids"(自定义)遍历
二、#{} 与 ${} 的核心区别
-
核心差异对比
| 对比维度 | #{} | ${} |
| 底层处理 | 预编译 SQL ,自动给值加英文单引号 | 直接字符串拼接,不加任何符号 |
| SQL 注入风险 | 无(参数作为占位符) | 有(恶意参数可篡改语法) |
| 取值限制 | 支持所有类型 | 不支持单独的基本类型(需封装 Bean/Map) |
| 使用场景 | 普通参数传递 | 动态字段名/表名(order by、group by) |
-
实操实例
-
#{}
<!-- 参数为"admin",最终SQL自动加单引号 -->
select * from user where username=#{username}
<!-- 解析后SQL:select * from user where username='admin' -->
-
${}
<!-- 参数为"sal",直接拼接为字段名,不加单引号 -->
select * from user order by ${sortField}
<!-- 解析后SQL:select * from user order by sal -->
注意:${}不能用于用户输入参数(用户名、密码),否则会引发 Sql 注入
三、动态 SQL
动态 SQL 通过标签自动合法地拼接 SQL ,解决“多条件查询、批量操作、动态更新”的语法问题,无需手动拼接字符串
-
if 标签(条件判断)
-
作用:根据参数是否有效(非空、非空字符串),决定是否拼接该 SQL
-
语法:test 属性写 OGNL 表达式
<if test="name != null and name != ''">
and name like concat('%', #{name}, '%') <!-- 模糊查询 -->
</if>
<if test="minSal != null">
and sal >= #{minSal} <!-- 数值类型无需判断空字符串 -->
</if>
-
关键:test 属性中直接用参数名(与传递方式一致),无需加 #{}
-
where 标签(条件拼接优化)
-
问题:当多个 if 标签进行拼接时,可能出现 “where and ...” 的语法错误(第一个条件带 and)
-
作用:自动去除条件开头多余的 and/or ,无有效条件时自动忽略 where 字句
<select id="queryBySal" resultType="cn.wolfcode.user.domain.Employee">
select * from employee
<where>
<if test="minSal != null">
and sal >= #{minSal}</if>
<if test="maxSal != null">
and sal <= #{maxSal}</if> <!-- XML中<=转义为<= -->
</where>
</select>
-
当只有一个生效时,会自动去掉 and
-
set 标签(动态更新)
-
问题:动态更新时,可能出现“set name=?, sn=?,” 的语法错误(最后一个字段带逗号)
-
作用:自动去除更新字段末尾多余的逗号,无有效字段时不拼接 set 字句
<update id="edit">
update employee
<set>
<if test="name != null and name != ''">name=#{name},</if>
<if test="sn != null and sn != ''">sn=#{sn},</if>
<if test="sal != null">sal=#{sal}</if>
</set>
where id=#{id}
</update>
-
效果:仅更新传入的非空字段,未传入字段保持原样
-
foreach 标签(批量操作)
-
作用:遍历集合/数组,拼接批量 SQL
-
核心属性
-
collection:遍历的集合/数组名(默认 list/array ,若有设置的注解@Param 指定名用指定名)
-
item:集合元素的别名(遍历过程中,每个元素的临时名称)
-
separator:元素间的分割符
-
open:拼接的起始字符串
-
close:拼接结束字符串
<delete id="batchDelete">
delete from employee
<foreach collection="ids" item="id" open="where id in (" separator="," close=")">
#{id}
</foreach>
</delete>
-
效果:传入
List<Long> ids = Arrays.asList(1L,2L,3L),生成delete from employee where id in (1,2,3),若没有有效值会直接忽略 foreach 标签删除整个表
浙公网安备 33010602011771号