TypeHandler
TypeHandler的主要工作是完成对Java代码和数据库中类型的映射,完成PrepareStatement参数的填充(JavaType->JDBCType)和ResultSet的解析(JDBCType->JavaType)。
public interface TypeHandler<T> {
//设置参数
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
//取得结果,供普通select用
T getResult(ResultSet rs, String columnName) throws SQLException;
//取得结果,供普通select用
T getResult(ResultSet rs, int columnIndex) throws SQLException;
//取得结果,供存储过程用(stored procedure)
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
Mybatis自身提供了基础Java对象的TypeHandler。
BaseTypeHandler-自定义TypeHandler
主要通过对org.apache.ibatis.type.BaseTypeHandler的继承,可以实现对Java字段类型和JDBC类型的设置和转换。BaseTypeHandler对TypeHandler进行了封装,增加了入参为空或返回值为空的预处理。
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
protected Configuration configuration;
public void setConfiguration(Configuration c) {
this.configuration = c;
@Override
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
if (jdbcType == null) { // 检查jdbcType
throw new TypeException("方便阅读,已删减");
try {
ps.setNull(i, jdbcType.TYPE_CODE); // 检查jdbcType
} catch (SQLException e) {
throw new TypeException("方便阅读,已删减 " + e, e);
} else {
setNonNullParameter(ps, i, parameter, jdbcType);
@Override
public T getResult(ResultSet rs, String columnName) throws SQLException {
T result = getNullableResult(rs, columnName);
if (rs.wasNull()) {
return null;
} else {
return result;
@Override
public T getResult(ResultSet rs, int columnIndex) throws SQLException {
T result = getNullableResult(rs, columnIndex);
if (rs.wasNull()) {
return null;
} else {
return result;
@Override
public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
T result = getNullableResult(cs, columnIndex);
if (cs.wasNull()) {
return null;
} else {
return result;
// 对JDBC PreparedStatement中的参数设定交给子类完成
public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
// 对返回值的处理交给子类完成
public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;
public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;
以BigDecimalTypeHandler为例
public class BigDecimalTypeHandler extends BaseTypeHandler<BigDecimal> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, BigDecimal parameter, JdbcType jdbcType)
throws SQLException {
ps.setBigDecimal(i, parameter);
@Override
public BigDecimal getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return rs.getBigDecimal(columnName);
@Override
public BigDecimal getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return rs.getBigDecimal(columnIndex);
@Override
public BigDecimal getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return cs.getBigDecimal(columnIndex);
BaseTypeHandler和TypeHandler的继承关系
TypeHandler的注册
Mybatis中所有自定义或内置基础的TypeHandler,都保存在org.apache.ibatis.type.TypeHandlerRegistry中。
初始化TypeHandlerRegistry对象时,完成基础类型的注册
TypeHandlerRegistry中,包含3个Map和一个 UNKNOWN_TYPE_HANDLER:
JDBC_TYPE_HANDLER_MAP:Map<JdbcType, TypeHandler<?>> 支持按JDBC查找TypeHandler
TYPE_HANDLER_MAP:Map<Type, Map<JdbcType, TypeHandler<?>>> 支持按JavaType查找TypeHandler
ALL_TYPE_HANDLERS_MAP:Map<Class, TypeHandler> 支持按TypeHandler.class查找TypeHandler
UNKNOWN_TYPE_HANDLER:当查询不到对应的TypeHandler时作为默认TypeHandler;设置参数时执行逻辑按ObjectTypeHandler中的参数设定逻辑执行
TypeHandlerRegistry并提供了多种register方法,以config解析XMLConfigBuilder类中的typeHandlerElement方法为例:
一段TypeHandler配置
XMLConfigBuilder类中的typeHandlerElement方法
private void typeHandlerElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
// package 解析
if ("package".equals(child.getName())) {
String typeHandlerPackage = child.getStringAttribute("name");
//调用TypeHandlerRegistry.register,去包下找所有类
typeHandlerRegistry.register(typeHandlerPackage);
} else {
// 单个 typeHandler
String javaTypeName = child.getStringAttribute("javaType");
String jdbcTypeName = child.getStringAttribute("jdbcType");
String handlerTypeName = child.getStringAttribute("handler");
Class<?> javaTypeClass = resolveClass(javaTypeName);
JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
Class<?> typeHandlerClass = resolveClass(handlerTypeName);
// 调用TypeHandlerRegistry.register
if (javaTypeClass != null) {
if (jdbcType == null) {
// 只指定javaType
// jdbcType 从@MappedJdbcTypes解析
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
// 同时指定javaType jdbcType
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
} else {
// 按 handler类名,
// javaType 从@MappedTypes 或 向上遍历TypeReference取泛型参数的类型
// jdbcType 从@MappedJdbcTypes
typeHandlerRegistry.register(typeHandlerClass);
TypeHandler的获取
getTypeHandler的方法
public TypeHandler<?> getTypeHandler(JdbcType jdbcType) {
return JDBC_TYPE_HANDLER_MAP.get(jdbcType);
private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
// 按JavaType查询JdbcType Map
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
TypeHandler<?> handler = null;
if (jdbcHandlerMap != null) {
// 在JdbcType Map获取最终的TypeHandler
handler = jdbcHandlerMap.get(jdbcType);
if (handler == null) {
// 拿默认Handler
handler = jdbcHandlerMap.get(null);
if (handler == null && type != null && type instanceof Class && Enum.class.isAssignableFrom((Class<?>) type)) {
handler = new EnumTypeHandler((Class<?>) type);
// type drives generics here
return (TypeHandler<T>) handler;
参数与Handler绑定
Mapper解析时,Mybatis将语句中的不同数据类型的JdbcType、JavaType和解析出的TypeHandler一同组成ParameterMapping对象;对象绑定到对应的SqlSource中,保存到全局Configuration中的MappedStatement中。
本部分列出JavaType和JdbcType解析位置的核心代码,如果对Mapper和Config解析全流程感兴趣的同学可以从下边的入口进入。
(入口:XMLConfigBuilder->XMLMapperBuilder->XMLStatementBuilder->XMLLanguageDriver->RawSQLSource/DynamicSqlSource->SQLSourceBuilder->ParameterMappingTokenHandler)
ParameterExpression对象的结构
解析动态入参的类型和TypeHandler
private static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {
@Override
public String handleToken(String content) {
parameterMappings.add(buildParameterMapping(content));
// jdbc Preparestatement参数占位 ?, ?, ?
return "?";
private ParameterMapping buildParameterMapping(String content) {
//content = "name, JavaType=String, JdbcType=VARCHAR"
//先解析参数映射,就是转化成一个hashmap
// 代码简化应该是通过方法拿到 ParameterExpression(继承HashMap)对象;
Map<String, String> propertiesMap = new ParameterExpression(content)
// 省略部分代码....
ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
Class<?> javaType = propertyType;
String typeHandlerAlias = null;
for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
String name = entry.getKey();
String value = entry.getValue();
if ("javaType".equals(name)) {
javaType = resolveClass(value); // 解析JavaType
builder.javaType(javaType);
} else if ("jdbcType".equals(name)) { // 解析JdbcType
builder.jdbcType(resolveJdbcType(value));
} else if ("mode".equals(name)) {
builder.mode(resolveParameterMode(value));
} else if ("numericScale".equals(name)) {
builder.numericScale(Integer.valueOf(value));
} else if ("resultMap".equals(name)) {
builder.resultMapId(value);
} else if ("typeHandler".equals(name)) {
typeHandlerAlias = value; // 解析typeHandler
} else if ("jdbcTypeName".equals(name)) {
builder.jdbcTypeName(value);
} else if ("property".equals(name)) {
// Do Nothing
} else if ("expression".equals(name)) {
throw new BuilderException("Expression based parameters are not supported yet");
} else {
throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content + "}. Valid properties are " + parameterProperties);
// 如果 #{} 参数中指定了typeHandler直接用
// name, JavaType=String, JdbcType=VARCHAR, typeHandler=FastjsonListTypeHandler
if (typeHandlerAlias != null) {
builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
return builder.build();
ParameterMapping的build方法和Handler对象的选定
public class ParameterMapping {
private String property;
private ParameterMode mode;
// javaType
private Class<?> javaType = Object.class;
// jdbcType
private JdbcType jdbcType;
private Integer numericScale;
// handler Mapper解析时已经完成handler的选定
private TypeHandler<?> typeHandler;
private String resultMapId;
private String jdbcTypeName;
private String expression;
public ParameterMapping build() {
resolveTypeHandler();
validate();
return parameterMapping;
// 至此 完成参数填充阶段的 ParameterMapping 的构建
private void resolveTypeHandler() {
if (parameterMapping.typeHandler == null && parameterMapping.javaType != null) {
Configuration configuration = parameterMapping.configuration;
// 1.从全局configuration中拿到TypeHandlerRegistry
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
// 2.拿TypeHandler
parameterMapping.typeHandler = typeHandlerRegistry.getTypeHandler(parameterMapping.javaType, parameterMapping.jdbcType);
使用TypeHandler设置参数
执行查询时,会从MappedStatement中获取SourceSql,并以SourceSql构建BoundSql对象。
// Executor中的query方法
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//得到绑定sql
BoundSql boundSql = ms.getBoundSql(parameter);
//创建缓存Key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
public BoundSql getBoundSql(Object parameterObject) {
//其实就是调用sqlSource.getBoundSql
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
for (ParameterMapping pm : boundSql.getParameterMappings()) {
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = configuration.getResultMap(rmId);
if (rm != null) {
hasNestedResultMaps |= rm.hasNestedResultMaps();
return boundSql;
public class BoundSql {
private String sql;
private List<ParameterMapping> parameterMappings;
private Object parameterObject;
private Map<String, Object> additionalParameters;
private MetaObject metaParameters;
利用BoundSql对象中的信息,在执行query前对入参做替换。
public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException {
Statement stmt = null;
try {
flushStatements();
Configuration configuration = ms.getConfiguration();
// 创建语句的Handler,
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection);
// 处理参数
handler.parameterize(stmt);
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
public void setParameters(PreparedStatement ps) throws SQLException {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
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) { // IN 入参 OUT出参,resultMap
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())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
// 调用TypeHandler的setParameter
typeHandler.setParameter(ps, i + 1, value, jdbcType);
至此,TypeHandler注册、获取和在参数设定的部分完成。
PreparedStatement 预编译SQL:docs.oracle.com/javase/8/do…
Mybatis TypeHandler:mybatis.org/mybatis-3/c…