MyBatis 参数传递详解:从基础到复杂场景全解析

一、参数传递概述:为什么参数处理如此重要?

在 MyBatis 开发中,参数传递是连接 Java 代码与 SQL 语句的桥梁,直接影响 SQL 执行的正确性。不合理的参数传递方式可能导致:

  • SQL 语法错误
  • 参数值无法正确映射
  • 潜在的 SQL 注入风险
  • 代码可读性和可维护性下降

MyBatis 提供了灵活多样的参数传递方式,能应对从简单到复杂的各种业务场景。本文将系统讲解 MyBatis 的参数处理机制,涵盖基本类型、对象、集合等多种参数类型,并通过实例展示最佳实践。

二、单个参数传递:最简单的参数处理方式

当 Mapper 接口方法只有一个参数时,MyBatis 的处理方式最为灵活,无需额外配置即可直接使用。

2.1 基本类型参数

Mapper 接口

java

运行

// 根据ID查询用户
User getUserById(Integer id);

映射文件

xml

<select id="getUserById" resultType="User">
    SELECT * FROM user WHERE id = #{id}
</select>

说明

  • #{id}中的名称可以任意,如#{xxx}也能正常工作
  • MyBatis 会自动根据参数类型进行类型转换
  • 支持的基本类型包括:intlongStringboolean

2.2 字符串参数与模糊查询

Mapper 接口

java

运行

// 根据用户名模糊查询
List<User> getUserByUsername(String username);

映射文件

xml

<select id="getUserByUsername" resultType="User">
    SELECT * FROM user WHERE username LIKE CONCAT('%', #{username}, '%')
</select>

注意

  • MySQL 中使用CONCAT()函数拼接通配符
  • Oracle 中可直接使用'%'||#{username}||'%'
  • 避免使用字符串拼接(${username})防止 SQL 注入

2.3 单个对象参数

当参数是一个 Java 对象时,#{}中需要使用对象的属性名:

Mapper 接口

java

运行

// 根据用户对象查询(演示用)
User getUserByUser(User user);

映射文件

xml

<select id="getUserByUser" resultType="User">
    SELECT * FROM user WHERE username = #{username} AND age = #{age}
</select>

说明

  • #{username}对应 User 对象的getUsername()方法
  • 必须保证对象有对应的 Getter 方法
  • 参数类型可省略(MyBatis 会自动推断)

三、多个参数传递:@Param 注解的正确使用

当方法有多个参数时,MyBatis 无法直接识别参数名称,需要使用@Param注解显式指定。

3.1 多个基本类型参数

Mapper 接口

java

运行

// 多条件查询
List<User> getUserByAgeAndEmail(
    @Param("minAge") Integer minAge,
    @Param("maxAge") Integer maxAge,
    @Param("email") String email
);

映射文件

xml

<select id="getUserByAgeAndEmail" resultType="User">
    SELECT * FROM user 
    WHERE age BETWEEN #{minAge} AND #{maxAge}
    AND email LIKE CONCAT('%', #{email}, '%')
</select>

关键点

  • @Param注解中的值必须与#{}中的名称一致
  • 不使用@Param时,MyBatis 会使用arg0arg1param1param2作为参数名
  • 推荐始终使用@Param,提高代码可读性

3.2 混合参数类型

方法参数可以是基本类型、对象、集合等的混合:

Mapper 接口

java

运行

// 混合参数查询
List<User> getMixedParams(
    @Param("user") User user,
    @Param("status") Integer status,
    @Param("roles") List<String> roles
);

映射文件

xml

<select id="getMixedParams" resultType="User">
    SELECT * FROM user 
    WHERE username = #{user.username}
    AND status = #{status}
    AND role IN 
    <foreach collection="roles" item="role" open="(" separator="," close=")">
        #{role}
    </foreach>
</select>

说明

  • 引用对象属性时使用#{对象名.属性名}格式
  • 集合参数可配合foreach标签使用

四、特殊参数类型:数组、集合与 Map

4.1 数组参数

Mapper 接口

java

运行

// 根据ID数组查询
List<User> getUserByIds(@Param("ids") Integer[] ids);

映射文件

xml

<select id="getUserByIds" resultType="User">
    SELECT * FROM user 
    WHERE id IN 
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

4.2 List 集合参数

Mapper 接口

java

运行

// 根据ID列表查询
List<User> getUserByIdList(@Param("idList") List<Integer> idList);

映射文件

xml

<select id="getUserByIdList" resultType="User">
    SELECT * FROM user 
    WHERE id IN 
    <foreach collection="idList" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

foreach 标签属性说明

  • collection:集合参数名称
  • item:循环变量名
  • open:开始符号
  • separator:分隔符
  • close:结束符号
  • index:索引变量名(可选)

4.3 Map 参数

当参数较多且无合适实体类时,可使用 Map 传递参数:

Mapper 接口

java

运行

// 使用Map查询
List<User> getUserByMap(@Param("params") Map<String, Object> params);

映射文件

xml

<select id="getUserByMap" resultType="User">
    SELECT * FROM user 
    <where>
        <if test="params.username != null">
            AND username LIKE CONCAT('%', #{params.username}, '%')
        </if>
        <if test="params.age != null">
            AND age = #{params.age}
        </if>
    </where>
</select>

使用示例

java

运行

Map<String, Object> params = new HashMap<>();
params.put("username", "张");
params.put("age", 20);
List<User> users = userMapper.getUserByMap(params);

五、高级参数处理:ResultMap 与复杂对象映射

当数据库表字段与 Java 对象属性名不一致时,需要使用ResultMap进行自定义映射。

5.1 字段与属性名不一致问题

数据库表字段user_nameuser_ageJava 对象属性usernameage

5.2 使用 ResultMap 解决映射问题

映射文件

xml

<!-- 定义ResultMap -->
<resultMap id="UserResultMap" type="User">
    <id property="id" column="id"/>
    <result property="username" column="user_name"/>
    <result property="age" column="user_age"/>
    <result property="email" column="email"/>
</resultMap>

<!-- 使用ResultMap -->
<select id="getUserById" resultMap="UserResultMap">
    SELECT id, user_name, user_age, email FROM user WHERE id = #{id}
</select>

说明

  • id标签用于映射主键
  • result标签用于映射普通字段
  • property:Java 对象属性名
  • column:数据库表字段名

5.3 关联查询映射

处理一对一、一对多关系时的参数传递:

一对一映射

xml

<resultMap id="OrderResultMap" type="Order">
    <id property="id" column="id"/>
    <result property="orderNo" column="order_no"/>
    <!-- 关联用户对象 -->
    <association property="user" javaType="User">
        <id property="id" column="user_id"/>
        <result property="username" column="username"/>
    </association>
</resultMap>

一对多映射

xml

<resultMap id="UserOrderResultMap" type="User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <!-- 关联订单列表 -->
    <collection property="orders" ofType="Order">
        <id property="id" column="order_id"/>
        <result property="orderNo" column="order_no"/>
    </collection>
</resultMap>

六、参数传递的安全问题:#{} 与 ${} 的区别

MyBatis 提供两种参数占位符:#{}${},它们的区别直接关系到 SQL 安全。

6.1 #{}:预编译处理

  • 会将参数替换为?,进行预编译
  • 自动处理类型转换
  • 防止 SQL 注入
  • 适用于大多数参数传递场景

示例

xml

SELECT * FROM user WHERE username = #{username}

编译后

sql

SELECT * FROM user WHERE username = ?

6.2 ${}:字符串替换

  • 直接替换字符串,不进行预编译
  • 存在 SQL 注入风险
  • 适用于动态表名、排序字段等场景

示例

xml

SELECT * FROM ${tableName} ORDER BY ${column} ${order}

替换后

sql

SELECT * FROM user ORDER BY age DESC

6.3 安全使用 ${} 的场景

  1. 动态表名

xml

<select id="getByTable" resultType="User">
    SELECT * FROM ${tableName} WHERE id = #{id}
</select>
  1. 动态排序

xml

<select id="getAllOrderBy" resultType="User">
    SELECT * FROM user ORDER BY ${column} ${direction}
</select>

安全建议

  • ${}参数进行白名单校验
  • 避免直接使用用户输入作为${}参数
  • 优先使用#{},仅在必要时使用${}

七、参数传递最佳实践

  1. 保持参数数量精简

    • 超过 3 个参数时,考虑封装为实体类
    • 零散参数较多时,使用 Map 或 DTO 对象
  2. 始终使用 @Param 注解

    • 提高代码可读性
    • 避免参数顺序变化导致的错误
  3. 合理使用 ResultMap

    • 字段名与属性名不一致时必须使用
    • 关联查询时显式定义映射关系
  4. 防止 SQL 注入

    • 优先使用#{}
    • ${}参数进行严格校验
  5. 复杂参数的文档说明

    • 对 Map 参数和复杂对象添加注释
    • 说明每个参数的含义和用途

八、常见问题与解决方案

  1. Parameter 'xxx' not found

    • 检查是否缺少@Param注解
    • 确保@Param值与#{}中的名称一致
  2. 无效的参数类型转换

    • 检查参数类型与数据库字段类型是否匹配
    • 考虑使用类型处理器(TypeHandler)
  3. 关联查询结果为 null

    • 检查ResultMap中的关联配置
    • 确保 JOIN 查询的关联条件正确
  4. SQL 注入漏洞

    • ${}替换为#{}
    • 对动态参数进行白名单验证

总结

参数传递是 MyBatis 开发中的基础而重要的环节,本文详细介绍了各种参数类型的传递方式,包括基本类型、对象、集合等,以及ResultMap的使用和 SQL 安全问题。掌握这些知识,能够帮助你处理各种复杂的参数场景,编写安全、高效的 MyBatis 代码。在实际开发中,应根据具体业务场景选择合适的参数传递方式,并始终注意 SQL 安全问题。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值