使用PreparedStatement或者CallableStatement对执行SQL语句时,如果SQL语句中有参数占位符,那么在之前就需要为参数占位符设置值。ParameterHandler的主要职责就是给参数占位符赋值,在赋值时又结合使用了TypeHandler来对目标参数进行Java和JDBC类型之间的转换。
ParameterHandler相对于Mybatis其它的组件来讲是相比较简单的,其接口只定义了两个方法:
- getParameterObject,用于获取执行Mapper时传入的参数对象;
- setParameters,用于为PreparedStatement或者CallableStatement对象设置参数值。
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps) throws SQLException;
}
DefaultParameterHandler
Mybatis为ParameterHandler提供了一个默认实现类-DefaultParameterHandler,其实现了getParameterObject方法和setParameters方法。getParameterObject方法的实现逻辑相当简单,就是将构造方法传入的parameterObject原样返回。
setParameters方法的实现逻辑相比之下就要复杂很多,DefaultParameterHandler首先获取Mapper配置中的参数映射ParameterMapping;然后遍历所有ParameterMapping,根据其参数名称获取对应的值,最后调用当前参数对应的TypeHandler来为占位符赋值。
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance()
.activity("setting parameters")
.object(mappedStatement.getParameterMap().getId());
// 获取SQL中的占位符参数定义
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
// 如果类型处理器中有对应的typeHandler,直接赋值
value = parameterObject;
} else {
// 如果没有对应的typeHandler,将参数转化为MetaObject反射工具类
// 然后从元数据类中寻找当前参数的值
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 获取typeHandler类型处理器
TypeHandler typeHandler = parameterMapping.getTypeHandler();
// 获取JdbcType Java类型定义
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 调用typeHandler的setParameter方法完成占位符参数的赋值
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
结合源代码,可以概括为下述几个步骤:
- 从BoundSql类中获取当前SQL语句中的占位符参数ParameterMapping定义,如果不为空就直接遍历ParameterMapping定义,为每一个占位符参数赋值;
- 如果占位符参数的ParameterMode不是OUT就进行赋值处理;
- 如果在类型处理器中找到对应的typeHandler,就直接赋值;
- 如果在没有找到对应的typeHandler,将参数转化为MetaObject反射工具类,然后从元数据类中寻找当前参数的值;
- 得到value值之后,再获取在ParameterMapping中定义的typeHandler类型处理器和Java类型;
- 调用typeHandler类型处理器的setParameter方法,将value值设置到当前的PreparedStatement对象。