MyBatis之配置文件解析_类型处理器的处理
类型处理器接口TypeHandler
JDBC执行SQL语句时,使用PreparedStatement设置参数值的方法是setXxx方法,Xxx表示一种类型,所以设置参数值需要根据参数的类型使用不同的方法,mybatis中用这个类型对应的TypeHandler对象的setParameter方法统一实现,查询数据时,使用ResultSet的getXxx方法获得数据,根据查询列的不同类型调用不同的方法,mybatis用这个类型对应的TypeHandler对象的getResult方法统一实现。
// 类型处理器接口,T:Java类型
public interface TypeHandler<T> {
// 设置参数时调用,可以调用相应的ps中的方法
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
// 获得结果时调用
// 从rs中返回出给定的对象
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
抽象类BaseTypeHandler
BaseTypeHandler是一个抽象类,实现了TypeHandler接口,mybatis提供了大量的常用类型处理器,都是基于BaseTypeHandler的,位于包org.apache.ibatis.type中,如IntegerTypeHandler,EnumTypeHandler等等。
// BaseTypeHandler是一个抽象类,实现了TypeHandler接口
// TypeReference是一个抽象类,可以获得范型T的原始类型
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
// 实现了TypeHandler中的方法,在setParameter方法中处理了null的情型
// 定义了新的抽象方法
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;
}
类型处理器注册器
TypeHandlerRegistry是类型处理器注册器,用来注册各种不同类型的注册器,使用多个Map存储各种不同的类型处理器,比较复杂的情形是一个Java类型,可能对应多个JDBC类型,每个JDBC类型又对应了不同的TypeHandler,这使用Map<Type, Map<JdbcType, TypeHandler<?>>>表示,这种情形下允许JdbcType为null。TypeHandlerRegistry的数据结构如下:
public final class TypeHandlerRegistry {
// JdbcType为键,TypeHandler是值
private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
// 一个Java类型为键,值为一个Map,JdbcType为键,TypeHandler是值
// 同一个Java类型,对应不同的JdbcType-TypeHandler
private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
private final TypeHandler<Object> unknownTypeHandler;
// Class类型,对应的TypeHandler
private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();
private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();
// 默认的枚举类型处理器为EnumTypeHandler
private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;
}
TypeHandlerRegistry的register方法
TypeHandlerRegistry有多个重载的register方法来注册一个类型处理器
TypeHandlerRegistry主要方法包括注册类型处理器,及获得类型处理器的方法。
注册register方法的参数,有多个重载的register方法
// 多个重载的处理器的注册方法
// 基于Java类型的类型处理器的注册,3个参数:JavaType JdbcType handler
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
if (javaType != null) {
// javaType在typeHandlerMap是否已有值
Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
// 没有值,创建新的Map
if (map == null || map == NULL_TYPE_HANDLER_MAP) {
map = new HashMap<>();
}
// Z
map.put(jdbcType, handler);
typeHandlerMap.put(javaType, map);
}
// allTypeHandlersMap中增加新值
allTypeHandlersMap.put(handler.getClass(), handler);
}
// 首先看typeHandler上有无注解@MappedJdbcType,有则作为Jdbc类型,若没有则jdbcType作为null
// 2个参数:JavaType + TypeHandler
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler){}
// 一个参数: Class对象,表示一个TypeHandler类型,首先要找到这个类型处理器要处理的Java类型,
// 先看这个类型处理器类,有无注解@MappedTypes,若有则使用其定义的Java类型
// 否则使用反射获得TypeHandler<T>中的范型T的实际类作作为Java类型
public void register(Class<?> typeHandlerClass) {
boolean mappedTypeFound = false;
// 获得类型上的注解@MappedTypes
MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
if (mappedTypes != null) {
// 注解中属性value的值为javaTypeClass,进行注册
for (Class<?> javaTypeClass : mappedTypes.value()) {
register(javaTypeClass, typeHandlerClass);
mappedTypeFound = true;
}
}
if (!mappedTypeFound) {
// 如果没有注解@MappedTypes,使用反射获得TypeHandler<T>中的范型T的实际类作作为Java类型
register(getInstance(null, typeHandlerClass));
}
}
多个重载的getTypeHandler方法:能根据Java类型、JDBC类型获得对应的类型注册器对象
自定义类型处理器及解析
先写一个类,实现接口TypeHandler,也可以基于BaseTypeHandler实现,定义TypeHandler类时,可以用注解MappedTypes,定义多个Java类型,如果没有则定义TypeHandler上的范型类型,用MappedJdbcTypes注解定义多个JdbcType,当然也可使用XML文件配置时的javaType与jdbcType
其次在mybatis配置文件中使用typeHandlers标签注册,如下所示:
<typeHandlers>
<!-- 配置给定的类型处理器 -->
<typeHandler handler="com.beck.mybatis.entity.ArrayTypeHandler" jdbcType="VARCHAR" ></typeHandler>
<!-- 配置给定包中的所有类型处理器 -->
<package name="com.beck.mybatis"></package>
</typeHanders>
XMLConfigBuilder对象解析配置文件时,解析typeHandlers标签的方法是typeHandlerElement,其基本逻辑为:
若XML文件中定义了javaType属性,则使用其中的值作为javaType,其中的jdbcType属性有效
若没有定义javaType属性,则其中的jdbcType属性无效,此时Java类型为注解MappedTypes定义上的类型,若没有使用这个注解,则使用范型作为Java类型,若使用了注解MappedJdbcTypes,则使用所定义的JdbcType类型,若没有使用这个注解,则JdbcType为null。代码如下:
// 注册自定义的类型处理器,处理子标签typeHandlers
private void typeHandlerElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String typeHandlerPackage = child.getStringAttribute("name");
// 注册包下的所有类型处理器
typeHandlerRegistry.register(typeHandlerPackage);
} else { // 处理typeHandler子标签
// 获得javaType,jdbcType及handler的属性值
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);
// 若配置时没有定义javaType属性,则jdbcType的属性被忽略
if (javaTypeClass != null) {
// 若配置时没有定义java类型,所配置的jdbc类型也被忽略
if (jdbcType == null) {
//二个参数:java类型,handler,此时jdbcType为null
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
// 三个参数: java类型,jdbc类型,handler
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
// 一个参数:handler, 此时查看typeHandlerClass上的注解MappedTypes作为Java Type,注解MappedJdbcTypes作为Jdbc Type
// 若没有注解MappedTypes则用typeHandlerClass上的范型作为Java Type
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}
系统默认类型处理器的注册
mybatis初始化时,在SqlSessionFactoryBuilder的build方法中,会创建用来解析XML配置文件的对象XMLConfigBuilder对象,在创建这个对象时会首选创建Configuration对象,Configuration有一个TypeHandlerRegistry的引用,并创建了这个对象,TypeHandlerRegistry构造方法会注册多个mybatis默认的类型处理器。
public class Configuration {
// 持有TypeHandlerRegistry,并创建了TypeHandlerRegistry对象
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);
}
TypeHandlerRegistry的无参构造方法中,注册了系统默认的类型处理器。
public final class TypeHandlerRegistry {
public TypeHandlerRegistry(Configuration configuration) {
this.unknownTypeHandler = new UnknownTypeHandler(configuration);
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
// 注册了系统自定义的类型处理器
// ........
}
}
类型处理器的使用
当执行insert、update、delete SQL语句时,会调用参数类型对应的TypeHandler的setParameter方法,完成参数值的设置,使用逻辑在ParamHandler(默认实现类为DefaultParamHandler)的方法setParameters中
当执行查询语句时,会调用TypeHandler的getResult方法获得对应的结果,如果返回结果由ResultMap定义,则由DefaultResultSetHandler的getPropertyMappingValue方法由调用ResultMapping的TypeHandler的getResult方法获得结果。
若没有使用ResultMap,则使用DefaultResultSetHandler中的静态内部类UnMappedColumnAutoMapping的TypeHandler,调用其getResult方法获得要查询的值。DefaultResultSetHandler的getRow是获得查询数据的主控方法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix