Mybatis的七种传参方式示例全解析

好的,我们来对 MyBatis 的七种(或说常见几种)参数传递方式进行一次全面、清晰的解析。这对于掌握 MyBatis 的动态 SQL 编写至关重要。

首先,要明确一个核心概念:​MyBatis 如何确定传入的参数?​​ 这取决于你在 Mapper 接口方法上定义的参数类型、注解以及 XML 映射文件中使用的参数占位符(#{}${})。

图片[1]_Mybatis的七种传参方式示例全解析_知途无界

前置准备:示例实体类和接口

为了演示,我们先定义一个简单的 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 类似。

  • 接口方法​:参数是一个 MapList<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 映射​:foreachcollection 属性同样使用 @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>

重要提示​:如果方法只有一个参数且是 ListArray并且没有使用 @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}' 如果传入的 nameadmin' 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注入风险,慎用!​

核心建议​:

  1. 优先使用 #{},杜绝 SQL 注入。
  2. 多参数用 ​**@Param 注解**。
  3. 业务参数用 ​JavaBean
  4. 动态条件用 ​Map​ 或 <where>/<if> 配合 @Param
  5. 批量操作用 ​**List/Array + foreach**。
  6. ​**${} 仅在万不得已时使用,并做好过滤。​**
© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞36 分享
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容