MyBatis 通过 TypeHandlerRegistry
组件来集中 管理和查找 TypeHandler。 TypeHandler
是 MyBatis 中用于 处理 Java 类型和 JDBC 类型之间转换 的重要组件。 MyBatis 需要管理大量的 TypeHandler,并能够根据需要快速找到合适的 TypeHandler 来进行类型转换。
1. TypeHandlerRegistry
的作用:TypeHandler 的注册中心
TypeHandlerRegistry
可以被理解为 MyBatis 中 TypeHandler 的注册中心 或 TypeHandler 的仓库。 它的核心作用是:
- 注册 TypeHandler: 负责注册各种 TypeHandler 到 MyBatis 系统中。 TypeHandler 可以是 MyBatis 内置的,也可以是用户自定义的。
- 存储 TypeHandler: 内部维护一个数据结构 (实际上是多个
Map
) 来存储已注册的 TypeHandler,并按照不同的维度进行组织,方便查找。 - 查找 TypeHandler: 提供 API 方法,允许 MyBatis 在运行时根据 Java 类型和/或 JDBC 类型,快速查找并获取合适的 TypeHandler 实例。
2. TypeHandlerRegistry
如何管理 TypeHandler:内部存储结构
TypeHandlerRegistry
内部使用多个 Map
来存储 TypeHandler,以便根据不同的查找条件进行高效检索。 主要的存储结构包括:
KNOWN_TYPE_HANDLERS
(Map<JdbcType, TypeHandler<?>>):- Key:
JdbcType
枚举值,代表 JDBC 类型。 - Value:
TypeHandler<?>
实例。 - 作用: 根据 JDBC 类型查找 TypeHandler。 例如,当 MyBatis 需要处理
VARCHAR
类型的参数或结果时,会使用KNOWN_TYPE_HANDLERS
查找与JdbcType.VARCHAR
关联的 TypeHandler。
- Key:
TYPE_HANDLER_MAP
(Map<Type, Map<JdbcType, TypeHandler<?>>>):- Outer Key:
Type
对象,代表 Java 类型。 - Inner Key:
JdbcType
枚举值,代表 JDBC 类型 (可以为null
,表示不区分 JDBC 类型)。 - Value:
TypeHandler<?>
实例。 - 作用: 根据 Java 类型和 JDBC 类型查找 TypeHandler。 这是最主要的 TypeHandler 存储和查找结构。 MyBatis 会优先使用
TYPE_HANDLER_MAP
进行查找。 - 层级结构:
TYPE_HANDLER_MAP
是一个两层嵌套的Map
。 外层Map
的 Key 是 Java 类型,内层Map
的 Key 是 JDBC 类型。 这种结构允许根据 Java 类型和 JDBC 类型的组合来精确查找 TypeHandler。
- Outer Key:
ALL_TYPE_HANDLERS_MAP
(Map<Class<?>, TypeHandler<?>>):- Key:
Class<?>
对象,代表 TypeHandler 的 Class 类型。 - Value:
TypeHandler<?>
实例。 - 作用: 根据 TypeHandler 的 Class 类型查找 TypeHandler 实例。 主要用于通过
TypeHandlerRegistry.getTypeHandler(Class<? extends TypeHandler> handlerType)
方法直接根据 TypeHandler 类型查找实例。
- Key:
3. TypeHandler 的注册方式:
TypeHandler 可以通过多种方式注册到 TypeHandlerRegistry
中:
3.1. XML 配置文件 (
mybatis-config.xml
):<typeHandlers>
元素和<typeHandler>
子元素: 在mybatis-config.xml
文件中使用<typeHandlers>
元素和<typeHandler>
子元素来注册 TypeHandler。<typeHandlers> <typeHandler javaType="java.lang.String" jdbcType="VARCHAR" handler="com.example.typehandler.MyStringTypeHandler"/> <typeHandler package="com.example.typehandler.package"/> <!-- 扫描包注册 TypeHandler --> </typeHandlers>
javaType
属性 (可选): 指定 TypeHandler 处理的 Java 类型。 可以指定多个 Java 类型,用逗号分隔。jdbcType
属性 (可选): 指定 TypeHandler 处理的 JDBC 类型。 可以指定多个 JDBC 类型,用逗号分隔。handler
属性: 指定 TypeHandler 实现类的全限定名。<typeHandler package="...">
: 指定包名,MyBatis 会扫描指定包下的所有 TypeHandler,并自动注册。
XMLConfigBuilder
解析<typeHandlers>
元素时,会将配置的 TypeHandler 注册到Configuration
对象的typeHandlerRegistry
中。
3.2. Java 代码配置 (
Configuration
对象):Configuration.getTypeHandlerRegistry().register(...)
方法: 可以通过Configuration
对象的getTypeHandlerRegistry()
方法获取TypeHandlerRegistry
实例,然后使用register()
方法手动注册 TypeHandler。Configuration configuration = new Configuration(); TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); typeHandlerRegistry.register(String.class, JdbcType.VARCHAR, new MyStringTypeHandler()); // 注册指定 Java 类型和 JDBC 类型的 TypeHandler typeHandlerRegistry.register(MyEnumTypeHandler.class); // 注册 TypeHandler,不指定 Java 类型和 JDBC 类型 (MyEnumTypeHandler 需要标注 @MappedTypes 和 @MappedJdbcTypes) typeHandlerRegistry.register(new MyDefaultTypeHandler()); // 注册 TypeHandler 实例,不指定 Java 类型和 JDBC 类型 (MyDefaultTypeHandler 需要标注 @MappedTypes 和 @MappedJdbcTypes)
register(TypeHandler<?> typeHandler)
: 注册 TypeHandler 实例。 TypeHandler 类需要使用@MappedTypes
和@MappedJdbcTypes
注解指定它处理的 Java 类型和 JDBC 类型。register(Class<?> javaType, TypeHandler<?> typeHandler)
: 注册指定 Java 类型的 TypeHandler 实例。register(JdbcType jdbcType, TypeHandler<?> typeHandler)
: 注册指定 JDBC 类型的 TypeHandler 实例。register(Class<?> javaType, JdbcType jdbcType, TypeHandler<?> typeHandler)
: 注册指定 Java 类型和 JDBC 类型的 TypeHandler 实例。register(String typeHandlerName)
: 根据 TypeHandler 的别名注册 TypeHandler (别名需要在<typeAliases>
中定义)。
3.3. 自动扫描包 (
<typeHandlers package="...">
):- 通过在
<typeHandlers>
元素中使用<typeHandler package="...">
子元素,可以指定包名。 MyBatis 会 自动扫描指定包及其子包下的所有 TypeHandler 实现类,并将它们注册到TypeHandlerRegistry
中。 - TypeHandler 类需要使用
@MappedTypes
和@MappedJdbcTypes
注解来声明它处理的 Java 类型和 JDBC 类型。
- 通过在
3.4. MyBatis 内置的 TypeHandler:
- MyBatis 框架内置了大量的常用 TypeHandler,用于处理 Java 基本类型、常用 Java 类型 (例如 String, Date, Enum) 和 JDBC 类型之间的转换。
- 这些内置的 TypeHandler 在
TypeHandlerRegistry
初始化时会被自动注册,无需显式配置。 例如:StringTypeHandler
,IntegerTypeHandler
,DateTypeHandler
,EnumTypeHandler
等。
4. TypeHandlerRegistry
如何查找 TypeHandler:查找策略
当 MyBatis 需要进行类型转换时 (例如设置 PreparedStatement 参数,获取 ResultSet 结果),会通过 TypeHandlerRegistry
查找合适的 TypeHandler。 TypeHandlerRegistry
的查找策略如下 (优先级从高到低):
4.1. 精确匹配 Java 类型和 JDBC 类型:
- MyBatis 会首先尝试 根据 Java 类型和 JDBC 类型的精确组合 在
TYPE_HANDLER_MAP
中查找 TypeHandler。 这是最精确的查找方式,如果找到,则直接使用该 TypeHandler。
- MyBatis 会首先尝试 根据 Java 类型和 JDBC 类型的精确组合 在
4.2. 匹配 Java 类型,忽略 JDBC 类型:
- 如果精确匹配 Java 类型和 JDBC 类型没有找到 TypeHandler,MyBatis 会尝试 只根据 Java 类型 在
TYPE_HANDLER_MAP
中查找 TypeHandler (JDBC 类型设置为null
)。 这种方式适用于某些 TypeHandler 只需要根据 Java 类型进行处理,而不需要区分 JDBC 类型的情况。
- 如果精确匹配 Java 类型和 JDBC 类型没有找到 TypeHandler,MyBatis 会尝试 只根据 Java 类型 在
4.3. 根据 JDBC 类型查找:
- 如果根据 Java 类型和 JDBC 类型都无法找到合适的 TypeHandler,MyBatis 会尝试 根据 JDBC 类型 在
KNOWN_TYPE_HANDLERS
中查找 TypeHandler。 这种方式适用于某些通用的 JDBC 类型处理场景,例如处理所有VARCHAR
类型的数据,可以使用与JdbcType.VARCHAR
关联的 TypeHandler。
- 如果根据 Java 类型和 JDBC 类型都无法找到合适的 TypeHandler,MyBatis 会尝试 根据 JDBC 类型 在
4.4. 类型继承关系查找 (Type Hierarchy):
- 如果以上方式都无法找到合适的 TypeHandler,MyBatis 还会 考虑 Java 类型的继承关系。 例如,如果查找
List
类型的 TypeHandler 失败,MyBatis 会尝试查找Collection
,Iterable
,Object
等父类型或接口的 TypeHandler。 这种方式可以提高 TypeHandler 的通用性。
- 如果以上方式都无法找到合适的 TypeHandler,MyBatis 还会 考虑 Java 类型的继承关系。 例如,如果查找
4.5. 默认 TypeHandler (Default TypeHandler):
- 如果经过上述所有查找策略都无法找到合适的 TypeHandler,MyBatis 会 使用默认的
ObjectTypeHandler
(或其他默认 TypeHandler) 进行处理。ObjectTypeHandler
可以处理Object
类型的数据,但可能会丢失类型信息,性能也可能较低。
- 如果经过上述所有查找策略都无法找到合适的 TypeHandler,MyBatis 会 使用默认的
5. 关键组件和类:
TypeHandlerRegistry
: TypeHandler 注册中心,负责管理和查找 TypeHandler。TypeHandler
接口: 所有 TypeHandler 实现类都需要实现的接口,定义了类型转换的方法 (setParameter()
,getResult()
).Configuration
对象:TypeHandlerRegistry
实例存储在Configuration
对象中。XMLConfigBuilder
: 在解析mybatis-config.xml
文件时,XMLConfigBuilder
负责解析<typeHandlers>
元素,并将配置的 TypeHandler 注册到Configuration
对象的typeHandlerRegistry
中。JdbcType
枚举: 定义了 JDBC 类型常量。Type
接口 (Java Reflection API): 表示 Java 类型。
总结 TypeHandlerRegistry
的作用和 TypeHandler 的管理和查找机制:
TypeHandlerRegistry
是 MyBatis 中 TypeHandler 的注册中心,负责管理和维护所有已注册的 TypeHandler。- TypeHandler 可以通过 XML 配置、Java 代码配置、自动扫描包和 MyBatis 内置等多种方式注册到
TypeHandlerRegistry
中。 TypeHandlerRegistry
使用多层Map
结构存储 TypeHandler,并提供高效的查找策略,可以根据 Java 类型、JDBC 类型和类型继承关系等条件查找合适的 TypeHandler。- MyBatis 在运行时需要进行类型转换时,会委托
TypeHandlerRegistry
查找合适的 TypeHandler,并使用找到的 TypeHandler 进行类型转换操作。 TypeHandlerRegistry
的管理和查找机制保证了 MyBatis 能够灵活、高效地处理各种 Java 类型和 JDBC 类型之间的转换,实现了类型转换的解耦和可扩展性。