好的,我们来对 MyBatis 的七种(或说常见几种)参数传递方式进行一次全面、清晰的解析。这对于掌握 MyBatis 的动态 SQL 编写至关重要。
首先,要明确一个核心概念:MyBatis 如何确定传入的参数? 这取决于你在 Mapper 接口方法上定义的参数类型、注解以及 XML 映射文件中使用的参数占位符(#{} 或 ${})。
![图片[1]_Mybatis的七种传参方式示例全解析_知途无界](https://zhituwujie.com/wp-content/uploads/2026/01/d2b5ca33bd20260125102822.png)
前置准备:示例实体类和接口
为了演示,我们先定义一个简单的 User 实体类和一个对应的 UserMapper 接口。
User.java
public class User {
private Long id;
private String name;
private Integer age;
private String email;
// 构造方法、Getter、Setter、toString() 省略
}
UserMapper.java (基础接口)
public interface UserMapper {
// 我们将在这个接口里定义各种传参方法
}
第一种:单个普通类型参数(Java 原生类型或String)
这是最简单的情况。当方法只有一个参数时,MyBatis 会直接将其传递给 SQL 语句,无需任何特殊处理。
- 接口方法:
User selectById(Long id); - XML 映射:可以直接使用
#{任意名称},因为只有一个参数,MyBatis 会忽略名称匹配。通常使用参数名本身,如#{id}。
<!-- UserMapper.xml -->
<select id="selectById" resultType="com.example.entity.User">
SELECT * FROM user WHERE id = #{id}
</select>
说明:这里的 #{id} 中的 id 可以换成 #{abc}、#{xyz},结果都一样,因为只有一个参数。但为了代码可读性,强烈建议使用与参数名相同的名称。
第二种:多个普通类型参数(@Param 注解)
当方法有多个参数时,直接使用 #{param1}, #{param2}… 虽然可行,但可读性极差。最佳实践是使用 @Param 注解为每个参数指定一个名称。
- 接口方法:使用
@Param注解为每个参数命名。User selectByNameAndAge(@Param("name") String name, @Param("age") Integer age); - XML 映射:使用
#{注解中指定的名称}来引用参数。
<select id="selectByNameAndAge" resultType="com.example.entity.User">
SELECT * FROM user WHERE name = #{name} AND age = #{age}
</select>
说明:@Param("name") 告诉 MyBatis,第一个参数的“逻辑名”是 name,在 XML 中通过 #{name} 访问。同理,#{age} 对应第二个参数。
第三种:传递 JavaBean 对象
当参数较多时,将它们封装到一个 JavaBean(如我们的 User 类)中是更好的选择。MyBatis 会自动从对象的属性中通过 Getter 方法取值。
- 接口方法:参数直接是一个对象。
int insertUser(User user); - XML 映射:使用
#{属性名}来引用 JavaBean 的属性。注意:这里用的是属性名(即 Getter/Setter 方法去掉 get/set 后的驼峰名),而不是数据库字段名。
<insert id="insertUser" parameterType="com.example.entity.User">
INSERT INTO user (name, age, email) VALUES (#{name}, #{age}, #{email})
</insert>
说明:parameterType 可以省略,MyBatis 可以通过反射推断出参数类型。#{name} 会调用 user.getName() 来获取值。
第四种:传递 Map 集合
Map 是一种灵活的键值对结构,可以动态地传递参数。其用法与 JavaBean 类似。
- 接口方法:参数是一个
Map。List<User> selectByMap(Map<String, Object> params); - XML 映射:使用
#{key}来引用 Map 中对应键的值。
<select id="selectByMap" resultType="com.example.entity.User">
SELECT * FROM user
WHERE
<!-- 假设Map中有 'minAge' 和 'maxAge' 两个键 -->
age >= #{minAge} AND age <= #{maxAge}
<!-- 还可以根据key是否存在来动态拼接条件 -->
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
</select>
调用示例:
Map<String, Object> params = new HashMap<>();
params.put("minAge", 18);
params.put("maxAge", 30);
params.put("name", "张");
List<User> users = userMapper.selectByMap(params);
说明:Map 的灵活性在于可以动态添加或移除键值对,非常适合不确定查询条件的场景。
第五种:使用 List 或 Array 集合参数(IN 查询)
当需要查询符合多个 ID(或其他值)的记录时,通常使用 IN 关键字,此时就需要传递一个集合。
对于 List:
- 接口方法:使用
@Param注解指定集合参数名。List<User> selectByIds(@Param("ids") List<Long> ids); - XML 映射:使用
foreach标签遍历集合。**collection属性的值必须是@Param注解指定的名称。**
<select id="selectByIds" resultType="com.example.entity.User">
SELECT * FROM user
WHERE id IN
<foreach collection="ids" item="id" index="index" open="(" separator="," close=")">
#{id}
</foreach>
</select>
**foreach 标签属性解析**:
collection:要遍历的集合/数组,值为@Param指定的名称或默认名称(见下文)。item:遍历过程中每个元素的别名,相当于循环中的变量名。index:遍历的索引(对于 List 是下标,对于 Map 是键)。open:循环开始的符号,例如(。separator:元素之间的分隔符,例如,。close:循环结束的符号,例如)。
对于 Array(数组):
- 接口方法:同样建议使用
@Param注解。List<User> selectByIdsArray(@Param("ids") Long[] ids); - XML 映射:
foreach的collection属性同样使用@Param指定的名称。<select id="selectByIdsArray" resultType="com.example.entity.User"> SELECT * FROM user WHERE id IN <foreach collection="ids" item="id" open="(" separator="," close=")"> #{id} </foreach> </select>
重要提示:如果方法只有一个参数且是 List 或 Array 并且没有使用 @Param 注解,MyBatis 会使用默认值:
List类型:collection属性值为list。Array类型:collection属性值为array。
但为了代码的可读性和一致性,强烈建议始终使用@Param注解。
第六种:混合参数(JavaBean + @Param)
有时,你需要结合固定的对象和额外的零散参数进行查询。这时可以将 JavaBean 与一个或多个用 @Param 注解标注的参数混合使用。
- 接口方法:
List<User> selectByUserAndStatus(@Param("user") User user, @Param("status") Integer status); - XML 映射:引用 JavaBean 的属性用
#{user.属性名},引用单独参数用#{status}。
<select id="selectByUserAndStatus" resultType="com.example.entity.User">
SELECT * FROM user
WHERE
name = #{user.name} AND
age = #{user.age} AND
status = #{status}
</select>
第七种:使用 $ 占位符(不推荐,仅作了解)
我们前面一直使用 #{},它是 预编译处理(PreparedStatement),会将参数替换为 ?,能有效防止 SQL 注入,是首选方式。
而 ${} 是 字符串替换(Statement),它会直接将参数值拼接到 SQL 语句中。
- 示例:
<!-- 危险!存在SQL注入风险 --> SELECT * FROM user WHERE name = '${name}'如果传入的name是admin' OR '1'='1,最终 SQL 会变成:SELECT * FROM user WHERE name = 'admin' OR '1'='1',这将查询出所有用户!
**${} 的唯一合理用途:在需要动态指定表名、列名**等 SQL 语句结构本身的地方。
<!-- 动态指定排序的列 -->
SELECT * FROM user ORDER BY ${columnName}
<!-- 分表查询 -->
SELECT * FROM user_${tableSuffix}
注意:即使在这些场景下,也必须对传入 ${} 的参数进行严格的白名单校验,确保其值是安全的、可控的。
总结与对比
| 传参方式 | 适用场景 | 优点 | 缺点 / 注意事项 |
|---|---|---|---|
| 1. 单个普通参数 | 简单查询,如 byId | 简单直观 | 无 |
| 2. 多个普通参数 + @Param | 少量离散参数查询 | 可读性好,推荐使用 | 参数多时繁琐 |
| 3. JavaBean 对象 | 参数多且属于同一实体 | 结构清晰,易于维护 | 需要创建额外的类 |
| 4. Map 集合 | 动态、不确定的查询条件 | 非常灵活 | 无编译期检查,易出错 |
| 5. List/Array (IN查询) | 批量查询、删除 | 方便处理集合 | 需用 foreach,注意 collection 属性 |
| 6. 混合参数 | 固定对象 + 额外参数 | 结合两者优点 | 写法稍复杂 |
| 7. ${} 占位符 | 动态表名、列名 | 可实现特殊需求 | 有SQL注入风险,慎用! |
核心建议:
- 优先使用
#{},杜绝 SQL 注入。 - 多参数用 **
@Param注解**。 - 业务参数用 JavaBean。
- 动态条件用 Map 或
<where>/<if>配合@Param。 - 批量操作用 **
List/Array+foreach**。 - **
${}仅在万不得已时使用,并做好过滤。**
























暂无评论内容