mybatis TypeHandler原理分析与思考
TypeHandler使用
- 在自定义TypeHandler上注解@MappedTypes(value = {A.class, B.class})
- 配置
typeHandlersPackage
. 包下的所有TypeHandler都会被自动注册. - Mapper.xml中声明
<result column="enum1" jdbcType="INTEGER" typeHandler="xxx"/>
- mybatis配置文件
<typeHandlers> <typeHandler handler="com.xxx.handler.EnumTypeHandler"/> </typeHandlers>
TypeHandler工作原理
- 自动注册
JavaType
的所有默认TypeHandler. 见TypeHandlerRegistry
- 虽然Enum不是
JavaType
, 但是会自动设置默认的EnumTypeHandler. 在根据javaType获取TypeHandler的时候, 会判断class是否是enum, 如果是, 则会生成该enum专用的EnumTypeHandler
- 虽然Enum不是
- TypeHandler注册时,需要寻找对应的JavaType.
- 如果已经指定JavaType, 则直接可以注册
- 如果没有指定, 则尝试通过@MappedTypes注解, 找到JavaType
- 如果依然找不到JavaType, 则判断该TypeHandler是否继承
TypeReference<T>
,通过getSuperclassTypeParameter
方法,可以得到泛型的JavaType. - 如果依然找不到JavaType, 则JavaType为null,进行注册.
- preparedStatement设值时, 调用来字段对应的HandlerType,进行转换.
- 查询数据库结果后, 调用字段对应的HandlerType.setResult.
mybatis中对于Enum的类型处理
使用者什么都不用做, 自动支持.
mybatis提供了EnumTypeHandler和EnumOrdinalTypeHandler.
- EnumTypeHandler: 调用Enum.name()方法, 将枚举转换为String.
- EnumOrdinalTypeHandler: 调用Enum.ordinal()方法, 将枚举转换为int.
当需要转换枚举类型时, 优先使用缓存中的TypeHandler, 如果没有找到, 则实时生成专用的EnumTypeHandler.
MybatisPlusEnumTypeHandler
MybatisPlus通过MybaitsEnumTypeHandler
增强对enum的支持,允许指定转换时使用的字段.
使用
- 配置
mybatis-plus.defaultEnumTypeHandler
, 默认是org.apache.ibatis.type.EnumTypeHandler
, 可以不配置. - 如果想要在初始化时, 创建TypeHandler, 有两种方式
- 配置
mybatis-plus.type-enums-package
. 自动为该包下的所有enum,注册MybatisPlusEnumTypeHandler. - 在model中为枚举字段注解@TableField(typeHandler = xxx), 为该枚举,注册指定的TypeHandler.
- 配置
- 如果想要在使用时, 实时创建Typehandler. 枚举实现IEnum接口,或者为想要序列化的字段设置@EnumValue注解.
所以,使用者只需要为枚举上需要转换的字添加@EnumValue即可, 无需做其他.
原理
启动项目时, 通过typeEnumsPackage配置的包名, 加载指定包下的所有枚举类型, 并将枚举类型注册到mybatis中.
- 扫描包下的所有枚举, 且枚举必须继承 IEnum(MybatisPlus提供的枚举接口).
- 根据枚举, 生成MybatisEnumTypeHandler实例, 并注册到mybatis中. 每个enum生成一个独有的MybatisEnumTypeHandler实例.
源码见MybatisSqlSessionFactoryBean.buildSqlSessionFactory()
mybatisPlus中枚举处理的另一种方式
指定enum(或任意javaType)使用jacksonTypeHandler, 使用json对enum序列化. 该方案简单而通用,可以对任意javaType序列化. 缺陷是 json序列化的格式不一定能满足需求.
延伸
问题:如何实现
泛型TypeHandler
? 答案是无法实现
,原因如下:
- 由于泛型擦除,所以需要为泛型类的所有子类,创建专用的TypeHandler. mybatis的
EnumTypeHandler
和mybatis-plus的MybatisEnumTypeHandler
是同样的原理, 只是创建的方式不同.
- mybatis是在getTypeHandler时, 为enum实时创建专用TypeHandler.
- mybatis-plus增加了: 初始化时, 通过type-enums-package,为包下的所有Enum创建专用TypeHandler.
- 由于mybatis-plus提供了@tableFiled字段,所以在model的字段上, 指定
泛型TypeHandler
, 这是否可行呢? 该方法不可用。Java的获取泛型参数,只能通过父类的泛型参数来获得。即只有在编译期明确指出的泛型参数,才可以获取到。 见[[java中继承与泛型]]