参考博文:MyBatis 自定义类型处理器 TypeHandler

它的主要作用:JavaType 与 JdbcType 之间的转换

  1. PreparedStatement 设置参数值
  2. 从 ResultSet 或 CallableStatement 中取出一个值

使用场景:当我们在 javabean 中自定义了枚举类型或者其它某个类型,但是在数据库中存储时往往需要 转换成数据库对应的类型,并且在从数据库中取出来时也需要 将数据库类型转换为 javabean 中的对应类型。比如:javabean 中字段类型为 Date,数据库中存储的是 varchar 类型;javabean 中字段类型是 Enum,数据库中存储的是 String 或者 Integer。

一般的做法是:存存前手动转换、取出时手动转换,但是比较繁琐。

为啥 Date 对象不用转换?那是因为 MyBatis 内置了场景的 转换器(页面最后/或则搜索 typeHandlers,可看到一个内置转换器列表),可以看看 TypeHandler 的子类有哪些,你就明白了

要自定义一个 TypeHandler,并生效,只需要两个步骤:

  1. 实现一个 TypeHandler:并设置 类型映射
  2. 将自定义的 TypeHandler 注册到 Mybatis 中

    将 TypeHandler 注册到 MyBatis 中的方式

第一种:application.yaml 中配置

  1. ## 设置自定义的 Typehandler 所在的包,启动的时候会自动扫描配置到 Mybatis 中
  2. mybatis:
  3. type-handlers-package: cn.mrcode.typehandler

第二种:如果不使用 boot 的自动配置的话

  1. @Bean("sqlSessionFactory")
  2. public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
  3. SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
  4. sqlSessionFactoryBean.setDataSource(dataSource);
  5. sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATOIN));
  6. org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
  7. // 自动将数据库中的下划线转换为驼峰格式
  8. configuration.setMapUnderscoreToCamelCase(true);
  9. configuration.setDefaultFetchSize(100);
  10. configuration.setDefaultStatementTimeout(30);
  11. sqlSessionFactoryBean.setConfiguration(configuration);
  12. //将 typehandler 注册到 mybatis
  13. // 以实例化方式注册
  14. GenderTypeHandler genderTypeHandler = new GenderTypeHandler();
  15. TypeHandler[] typeHandlers=new TypeHandler[]{genderTypeHandler};
  16. // 或则直接设置 typeHandler 所在包, 下面的实战例子就是使用这种方式
  17. // bean.setTypeHandlersPackage("cn.mrcode.rabbit.producer.entity.typehander");
  18. sqlSessionFactoryBean.setTypeHandlers(typeHandlers);
  19. return sqlSessionFactoryBean.getObject();
  20. }

第三种:在 mapper.xml 中自定

  1. <insert id="insertUser">
  2. insert into user_info(user_id,his_id,name,gender,password,create_time)
  3. values(#{userId,jdbcType=VARCHAR},#{hisId,jdbcType=VARCHAR},#{name,jdbcType=VARCHAR},
  4. #{gender,jdbcType=INTEGER,typeHandler=cn.mrcode.typehandler.GenderTypeHandler},#{password,jdbcType=VARCHAR},now())
  5. </insert>

:::tips 哪一种方式最好?
笔者这里不推荐使用第三种,这种方式太麻烦,貌似也没有相关的注解支持,只能在 xml 中指定。
其他两种都能自动识别,不需要额外的指定,只需要注册就可以了 :::

实战例子参考

步骤 2 的例子实战

步骤 2 中是程序编程方式,我们自己实例化 handler 实例,这样一来就可以将 handler 交给 ioc 容器管理,能面对更多的复杂场景

实战列子可以参考下下面这篇文章
消息可靠性投递 - 集成数据源

步骤 2 + 步骤 3 例子

该方案来自群友的实验

方法是这样的:

  1. 编写 TypeHandler 使用 @MappedTypes(value = {Message.class})

其中 Message.clas 你写一个空的类,但是具体的字段我们不使用这个类
在注册 typeHandler 的时候,就会使用 Message.class 作为字段类型去映射这个 TypeHandler,只要你查询/写入的类上有这个类型,框架会自动使用这个 TypeHandler 去处理

  1. 然后使用步骤 2 注册 TypeHandler
  2. 再使用 步骤 3 ,在需要的字段上使用该 TypeHandler:经过测试,会根据指定的 TypeHandler 类型去查找 已经注册的 TypeHandler,如果不存在,框架会自己实例化一个(new)

:::info 核心步骤:

  1. TypeHandler 使用 @MappedTypes 映射一个不使用的类型
  2. mapper.xml 中手动指定 TypeHandler, @MappedTypes 中指定的类型就会失效,这个规则在 mybatis 官方文档上有说明 ::: 这种方案在使用的时候麻烦了,但是可以在数据类型不变化的情况下使用,场景:比如加密后入库,查询出来先解密再被 程序读取(数据库字段类型是 varchar,程序是 String,程序这个字段类型不用变更为具体的类)