它的主要作用:JavaType 与 JdbcType 之间的转换
- PreparedStatement 设置参数值
- 从 ResultSet 或 CallableStatement 中取出一个值
使用场景:当我们在 javabean 中自定义了枚举类型或者其它某个类型,但是在数据库中存储时往往需要 转换成数据库对应的类型,并且在从数据库中取出来时也需要 将数据库类型转换为 javabean 中的对应类型。比如:javabean 中字段类型为 Date,数据库中存储的是 varchar 类型;javabean 中字段类型是 Enum,数据库中存储的是 String 或者 Integer。
一般的做法是:存存前手动转换、取出时手动转换,但是比较繁琐。
为啥 Date 对象不用转换?那是因为 MyBatis 内置了场景的 转换器(页面最后/或则搜索 typeHandlers,可看到一个内置转换器列表),可以看看 TypeHandler 的子类有哪些,你就明白了
要自定义一个 TypeHandler,并生效,只需要两个步骤:
第一种:application.yaml 中配置
## 设置自定义的 Typehandler 所在的包,启动的时候会自动扫描配置到 Mybatis 中
mybatis:
type-handlers-package: cn.mrcode.typehandler
第二种:如果不使用 boot 的自动配置的话
@Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATOIN));
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
// 自动将数据库中的下划线转换为驼峰格式
configuration.setMapUnderscoreToCamelCase(true);
configuration.setDefaultFetchSize(100);
configuration.setDefaultStatementTimeout(30);
sqlSessionFactoryBean.setConfiguration(configuration);
//将 typehandler 注册到 mybatis
// 以实例化方式注册
GenderTypeHandler genderTypeHandler = new GenderTypeHandler();
TypeHandler[] typeHandlers=new TypeHandler[]{genderTypeHandler};
// 或则直接设置 typeHandler 所在包, 下面的实战例子就是使用这种方式
// bean.setTypeHandlersPackage("cn.mrcode.rabbit.producer.entity.typehander");
sqlSessionFactoryBean.setTypeHandlers(typeHandlers);
return sqlSessionFactoryBean.getObject();
}
第三种:在 mapper.xml 中自定
<insert id="insertUser">
insert into user_info(user_id,his_id,name,gender,password,create_time)
values(#{userId,jdbcType=VARCHAR},#{hisId,jdbcType=VARCHAR},#{name,jdbcType=VARCHAR},
#{gender,jdbcType=INTEGER,typeHandler=cn.mrcode.typehandler.GenderTypeHandler},#{password,jdbcType=VARCHAR},now())
</insert>
:::tips
哪一种方式最好?
笔者这里不推荐使用第三种,这种方式太麻烦,貌似也没有相关的注解支持,只能在 xml 中指定。
其他两种都能自动识别,不需要额外的指定,只需要注册就可以了
:::
实战例子参考
步骤 2 的例子实战
步骤 2 中是程序编程方式,我们自己实例化 handler 实例,这样一来就可以将 handler 交给 ioc 容器管理,能面对更多的复杂场景
实战列子可以参考下下面这篇文章
消息可靠性投递 - 集成数据源
步骤 2 + 步骤 3 例子
该方案来自群友的实验
方法是这样的:
- 编写 TypeHandler 使用
@MappedTypes(value = {Message.class})
其中 Message.clas 你写一个空的类,但是具体的字段我们不使用这个类
在注册 typeHandler 的时候,就会使用 Message.class 作为字段类型去映射这个 TypeHandler,只要你查询/写入的类上有这个类型,框架会自动使用这个 TypeHandler 去处理
- 然后使用步骤 2 注册 TypeHandler
- 再使用 步骤 3 ,在需要的字段上使用该 TypeHandler:经过测试,会根据指定的 TypeHandler 类型去查找 已经注册的 TypeHandler,如果不存在,框架会自己实例化一个(new)
:::info 核心步骤:
- TypeHandler 使用 @MappedTypes 映射一个不使用的类型
- mapper.xml 中手动指定 TypeHandler, @MappedTypes 中指定的类型就会失效,这个规则在 mybatis 官方文档上有说明 ::: 这种方案在使用的时候麻烦了,但是可以在数据类型不变化的情况下使用,场景:比如加密后入库,查询出来先解密再被 程序读取(数据库字段类型是 varchar,程序是 String,程序这个字段类型不用变更为具体的类)