一、MyBatis参数传递的重要性
在持久层框架的使用过程中,参数传递犹如快递包装中的填充物——方式选择直接影响"数据包裹"能否安全到达SQL目的地。MyBatis作为半自动化的ORM框架,其参数处理机制既保留了灵活性又存在使用门槛,特别是在处理复杂参数场景时,开发者经常会遇到"明明参数传了值怎么报空指针"的灵魂拷问。
技术栈声明:本文使用MyBatis 3.5.9 + MySQL 8.0演示,JDK版本为11
二、单个参数的传递艺术
2.1 基本类型参数的优雅传递
<!-- 通过接口参数名直接引用 -->
<select id="selectUser" resultType="User">
SELECT * FROM users WHERE user_id = #{userId}
</select>
<!-- 没有在接口中定义参数名称时(需开启-parameters编译参数) -->
<select id="selectUser" resultType="User">
SELECT * FROM users WHERE user_id = #{arg0}
</select>
使用技巧:
- 建议为接口方法参数添加@Param注解增强可读性
- 基本类型参数的判空处理应特别注意:
#{age,jdbcType=INTEGER}
2.2 对象参数的深度访问
public class UserQuery {
private String username;
private Integer status;
// 省略getter/setter
}
// Mapper接口
User selectByCondition(UserQuery query);
<select id="selectByCondition" resultType="User">
SELECT * FROM users
WHERE username LIKE CONCAT('%',#{username},'%')
AND status = #{status}
</select>
嵌套属性访问示例:
<!-- 查询部门用户时 -->
<select id="selectDeptUsers" resultType="User">
SELECT * FROM users
WHERE dept_id = #{dept.deptId}
AND create_time > #{query.startTime}
</select>
2.3 Map参数的灵活运用
// Mapper接口
List<User> searchUsers(Map<String, Object> params);
<select id="searchUsers" resultType="User">
SELECT * FROM users
WHERE
<if test="minAge != null">age >= #{minAge}</if>
<if test="maxAge != null">AND age <= #{maxAge}</if>
<if test="roleList != null">
AND role_id IN
<foreach collection="roleList" item="role" open="(" separator="," close=")">
#{role}
</foreach>
</if>
</select>
特征验证:参数值为null时MyBatis的特殊处理机制
三、多个参数的组合拳法
3.1 @Param注解的魔法效应
// Mapper接口
List<User> selectByPage(@Param("offset") int offset,
@Param("size") int size,
@Param("orderBy") String orderBy);
<select id="selectByPage" resultType="User">
SELECT * FROM users
ORDER BY ${orderBy}
LIMIT #{offset}, #{size}
</select>
关键区别:
#{}
与${}
的SQL注入防护差异- 分页参数的类型转换隐患
3.2 隐式Map参数的妙用
<select id="selectComplex" resultType="User">
SELECT * FROM users
WHERE username = #{param1}
AND create_time BETWEEN #{param2.start} AND #{param2.end}
</select>
参数定位规则:
- param1/param2...的自然索引
- arg0/arg1...的编译参数
3.3 参数对象封装技法
// 分页查询参数包装对象
public class PageQuery {
private int current;
private int size;
private String keyword;
// 计算offset的getter方法
public int getOffset() {
return (current - 1) * size;
}
}
<select id="search" resultType="User">
SELECT * FROM users
WHERE username LIKE CONCAT('%',#{keyword},'%')
LIMIT #{offset}, #{size}
</select>
四、集合参数的进阶处理
4.1 List参数的遍历艺术
<update id="batchUpdateStatus">
UPDATE users SET status = #{status}
WHERE user_id IN
<foreach collection="userIds" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</update>
参数验证:foreach元素的必要属性检查清单
4.2 数组参数的奇幻漂流
// Mapper接口
int deleteByIds(Long[] ids);
<delete id="deleteByIds">
DELETE FROM users
WHERE user_id IN
<foreach collection="array" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
别名秘密:
- array → 数组参数
- list → List参数
- collection → 自定义参数名
4.3 Map包装的集合参数
// Mapper接口
int batchInsert(@Param("userList") List<User> users);
<insert id="batchInsert">
INSERT INTO users (username, email) VALUES
<foreach collection="userList" item="user" separator=",">
(#{user.username}, #{user.email})
</foreach>
</insert>
4.4 嵌套集合的特殊处理
// 部门-角色关联参数
public class DeptRoleParam {
private Long deptId;
private List<Integer> roleIds;
}
<insert id="insertDeptRoles">
INSERT INTO dept_role (dept_id, role_id) VALUES
<foreach collection="roleIds" item="roleId" separator=",">
(#{deptId}, #{roleId})
</foreach>
</insert>
五、关联技术深入:动态SQL
5.1 条件分支的优雅写法
<select id="dynamicSearch" resultType="User">
SELECT * FROM users
<where>
<choose>
<when test="username != null">
username = #{username}
</when>
<when test="email != null">
email LIKE CONCAT(#{email},'%')
</when>
<otherwise>
status = 1
</otherwise>
</choose>
</where>
</select>
5.2 SQL片段复用技术
<sql id="userBaseColumns">
user_id, username, email, create_time
</sql>
<select id="selectUserList" resultType="User">
SELECT
<include refid="userBaseColumns"/>,
department_name
FROM users u
JOIN departments d ON u.dept_id = d.dept_id
</select>
六、应用场景全景分析
- Web分页查询:封装分页参数对象
- 复杂条件搜索:Map参数配合动态SQL
- 批量数据操作:foreach元素的批量处理
- 多表关联查询:参数对象的多级嵌套
- 业务状态流转:带条件的参数更新
七、技术优缺点对比
优势维度:
- 参数处理方式的多样性:支持7种参数传递模式
- 类型转换的灵活性:自动处理基本类型与JDBC类型映射
- 动态SQL的配合能力:与OGNL表达式无缝集成
注意事项:
- 参数名为空时的定位困惑
- 集合参数的类型转换陷阱
- Map参数的类型安全危机
- 批量操作的性能天花板
八、参数传递的黄金法则
- 永远进行参数非空校验
- 优先使用参数对象封装
- 警惕${}的SQL注入风险
- 合理设置jdbcType类型
- 批量操作采用分批提交
九、避坑指南
- 隐式参数名的误会:未开启-parameters编译参数时不要依赖参数名
- 泛型擦除的陷阱:
<!-- 错误写法 -->
<foreach collection="list" item="item">...</foreach>
<!-- 正确写法 -->
<foreach collection="userList" item="user">...</foreach>
- 类型处理器的选择:自定义TypeHandler的注册问题
十、总结与展望
经过对MyBatis参数传递机制的全方位解析,我们可以清晰看到:
- 单个参数的简约性 VS 多参数的组合型
- 基础类型的直接性 VS 对象结构的组织性
- 集合操作的灵活性 VS 性能优化的必要性
随着MyBatis 3.5版本对参数处理的持续优化,参数验证机制和类型推导能力的增强,未来的参数传递将会更加智能和安全。开发者在掌握现有技术的同时,也要持续关注Param注解的增强特性等新变化。
评论