MyBatis ParameterHandler的具体使用
作者:灵魂猎手
一、概述
书接上回,StatementHandler参数处理&结果处理,分别委托给ParameterHandler&ResultSetHandler(注意不是ResultHandler)。那么本文介绍一下ParameterHandler。
本系列的第二章,我们讲了Mybatis的参数处理,那个参数处理,是把Mapper中各种可能的参数封装为Map,而这个参数处理,是把Map参数,设置到JDBC的Statement中。
二、源码
MyBatis中ParameterHandler只有一个实现DefaultParameterHandler,我们通过源码来解析其工作原理。
1 关键属性
DefaultParameterHandler 的关键属性:
MappedStatement:包含SQL元信息;parameterObject:Mapper方法传入的原始参数;BoundSql:包含解析后的 SQL 语句和参数映射列表;TypeHandlerRegistry:类型处理器注册表,用于查找合适的 TypeHandler。
public class DefaultParameterHandler implements ParameterHandler {
private final MappedStatement mappedStatement;
private final Object parameterObject;
private final BoundSql boundSql;
private final Configuration configuration;
private final TypeHandlerRegistry typeHandlerRegistry;
public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
this.mappedStatement = mappedStatement;
this.parameterObject = parameterObject;
this.boundSql = boundSql;
this.configuration = mappedStatement.getConfiguration();
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
}
}
2.setParameters
setParameters 是ParameterHandler的核心方法,负责将参数绑定到 PreparedStatement 中:
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// 获取SQL中的参数映射列表(每个元素对应一个占位符)
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
// 遍历每个参数映射,依次绑定到PreparedStatement
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
// 忽略输出参数(存储过程场景)
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty(); // 参数名称(如"id"、"user.name")
// 获取参数值:从原始参数对象中解析propertyName对应的 value
if (boundSql.hasAdditionalParameter(propertyName)) {
// 从附加参数中获取(<foreach>或<bind>标签可以提供额外参数)
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null; // 参数为null时直接设为null
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
// 参数本身就是TypeHandler可以处理的类型
value = parameterObject;
} else {
// 参数是复杂对象,通过反射获取propertyName对应的属性值
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 获取参数对应的TypeHandler(负责Java类型→JDBC类型的转换)
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 调用TypeHandler的方法,将value绑定到PreparedStatement的第i+1个占位符
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
这段代码的执行流程可分为三步:
- 解析参数映射列表(
ParameterMapping),每个元素对应 SQL 中的一个占位符 - 根据参数名称从参数对象中获取参数值(支持复杂对象的属性访问)
- 通过 TypeHandler 将参数值转换为 JDBC 类型并绑定到 PreparedStatement
AdditionalParameter这个额外参数是什么?
- foreach标签中的item、index都会被解析为额外参数;
- bind标签,也可以提供额外参数,你可以利用bind标签,使用OGNL表达式以外创建一个变量,并将其绑定到当前的上下文。
3.TypeHandler
TypeHandler(类型处理器)负责Java类型与JDBC类型之间的转换,不只是ParameterHandler处理参数需要他,ResultSetHandler处理返回结果时也需要他。
1.TypeHandler接口定义
通过接口定义就可以看出来,TypeHandler负责JAVA类型与JDBC类型互相转换。
public interface TypeHandler<T> {
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
2. 内置TypeHandler体系
MyBatis 提供了丰富的内置 TypeHandler,这里的例子只是冰山一角,有兴趣可以看下源码,一般情况下,是不需要自己自定义类型转换器的。具体实现这里不深入了,比较简单,模板模式,BaseTypeHandler实现了一些通用逻辑,子类就是JDBC API的各种调用。有兴趣可以直接看源码。
| 类型分类 | 典型实现类 | 功能说明 |
|---|---|---|
| 基本类型 | IntegerTypeHandler | 处理 Integer 与 JDBC INTEGER 转换 |
| 字符串类型 | StringTypeHandler | 处理 String 与 JDBC VARCHAR 转换 |
| 日期类型 | LocalDateTimeTypeHandler | 处理 Java 8 日期类型与 JDBC 日期类型转换 |
| 枚举类型 | EnumTypeHandler | 处理枚举的名称与 JDBC 类型转换 |
| 集合 / 数组类型 | ArrayTypeHandler | 处理 Java 数组与 JDBC ARRAY 类型转换 |
4. JdbcTypeForNull 的作用
jdbcTypeForNull是 MyBatis中用于处理null值参数的重要配置,当参数值为 null且未指定具体JDBC 类型时生效。
其主要作用是解决不同数据库对 null 值处理的兼容性问题,某些数据库,对 null 值的类型非常敏感,需要明确指定 JDBC 类型,比如Oracle,需要设置为NULL。
可以在xml中配置
<configuration>
<settings>
<!-- 设置null值默认的JDBC类型 -->
<setting name="jdbcTypeForNull" value="NULL"/>
</settings>
</configuration>
三、小结
ParameterHandler是MyBatis处理Statement的参数的核心组件,通过与 TypeHandler协作,实现了Java 类型到JDBC类型的灵活转换,支撑了MyBatis对各种复杂参数场景的处理能力。
到此这篇关于MyBatis ParameterHandler的具体使用的文章就介绍到这了,更多相关MyBatis ParameterHandler内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
您可能感兴趣的文章:
- MyBatis中传入参数parameterType类型详解
- Mybatis非配置原因,导致SqlSession was not registered for synchronization异常
- mybatis中批量插入的两种方式(高效插入)
- Mybatis中使用in()查询的方式详解
- Mybatis Update操作返回值问题
- MyBatis入门学习教程(一)-MyBatis快速入门
- MyBatis批量插入(insert)数据操作
- Mybatis plus实现Distinct去重功能
- Mybatis报错: org.apache.ibatis.exceptions.PersistenceException解决办法
