我是 码农小胖哥。天天有编程干货分享。觉得写的不错可以点个赞,转发一下,关注一下。
![3d248a36328ea5ce10d89ec2b7c50c42.png](https://img-blog.csdnimg.cn/img_convert/3d248a36328ea5ce10d89ec2b7c50c42.png)
1.场景
日常java开发中经常有这种需求,用0或者1这些代码(不局限于数字)来表示某种状态。比如用0表示女性,用1来表示男性。而且写入数据库可能是一个标识,从数据库读取又还原为具体的说明。而且一般情况下为了更好理解或者消除魔法值,通常的处理方案是定义一个枚举:
有些枚举是这样定义的
![c977c56b268c02235c017de745db36c9.png](https://img-blog.csdnimg.cn/img_convert/c977c56b268c02235c017de745db36c9.png)
那么通常很多人会这么入库(java伪代码)
![2138b56e5532796a5df480ffd3717428.png](https://img-blog.csdnimg.cn/img_convert/2138b56e5532796a5df480ffd3717428.png)
读取的时候要么同样按照上面的再反向处理一次或者使用数据库sql语法case when 来直接写入DTO
![48a4a57e79e27c72785ec5ee9453f6c6.png](https://img-blog.csdnimg.cn/img_convert/48a4a57e79e27c72785ec5ee9453f6c6.png)
这种处理方式看起来不是很优雅。而且多了很多的判断和处理逻辑,和我们的业务并不是非常相关。所以我们可以选择更好的处理方式。
2.Mybatis中的TypeHandler
如果你ORM框架用的是Mybatis。那么将很容易通过TypeHandler接口解决这个问题。
2.1 TypeHandler 分析
![9dc0ed144679dd4cd4f99297c47e4bf9.png](https://img-blog.csdnimg.cn/img_convert/9dc0ed144679dd4cd4f99297c47e4bf9.png)
源码分析:
- setParameter 方法 通过 传入的T类型写你自己的逻辑,选择调用 PreparedStatement 对象的某个set方法将数据写入数据库。此方法用来写库。
- getResult(ResultSet rs, String columnName) 通过字段名来读库并转换为T类型。
- getResult(ResultSet rs, int columnIndex) 通过字段索引来读库并转换为T类型。
- getResult(CallableStatement cs, int columnIndex) 调用存储过程来获取结果并转换为T类型。
2.2 EnumOrdinalTypeHandler
我们发现TypeHandler有一个实现类EnumOrdinalTypeHandler。字面意思是可以通过枚举的序号来处理类型。
![262231a7bd74480a714d4b705facef0d.png](https://img-blog.csdnimg.cn/img_convert/262231a7bd74480a714d4b705facef0d.png)
我们先不考虑setNull的情况。通过此方法我们发现确实存入的是枚举的顺序值(顺序从0开始),拿上面的例子来说 如果是GenderType.FEMALE是0,如果是GenderType.MALE是1,但是当GenderType.UNKNOWN时存入的是2。取的时候也是自然反向处理为具体的GenderType枚举。
2.3 EnumTypeHandler
我们还发现有另外一个枚举类型处理器。它的set方法是这样的:
![3073990a41e73d12c6c2a49af04e9f41.png](https://img-blog.csdnimg.cn/img_convert/3073990a41e73d12c6c2a49af04e9f41.png)
我们不考虑jdbcType问题发现都是将Enum.name()的值写入数据库。拿上面的例子来说 如果是GenderType.FEMALE是FEMALE,如果是GenderType.MALE是MALE,但是当GenderType.UNKNOWN时存入的是UNKNOWN。读库是通过Enum.valueOf(Class enumType,String name)来进行反转操作。
2.4 自定义TypeHandler
如果说我们的枚举类型或者说我们使用其他方式来处理类别转换怎么办?当然Mybatis不会帮你干这么具体的事情。需要你自己来实现了。我们还拿枚举作为例子,然后模仿上面的两种TypeHandler。
还是拿开始的例子来说通常我个人比较喜欢这么定义枚举:
![ae73967ff0a1ff9aba2e46b0124ad008.png](https://img-blog.csdnimg.cn/img_convert/ae73967ff0a1ff9aba2e46b0124ad008.png)
通过继承BaseTypeHandler实现该抽象类的3个钩子方法就行了:
![db44175b8a89716dca9faef427cebee1.png](https://img-blog.csdnimg.cn/img_convert/db44175b8a89716dca9faef427cebee1.png)
TypeHandler 实现写好了,那么如何让其发挥作用呢?我们接着往下走。
2.5 TypeHandler 配置
如果你在映射集xml配置文件比如 resultMap 的具有typeHandler 属性中 或者 Sql语句占位符#{} 中注册了typeHandler实现类全限定名。那么typeHandler 不需要再向TypeHandlerRegistry
中注册。因为系统会自动根据类全限定名实例化一个typeHandler,一定要保证无参构造器能够完整地工作。
另外一种是在配置中声明注册TypeHandler,如果你是xml配置需要在Configuration配置文件中的标签中进行声明式注册如下:
![0a8d17bd7ebcf44276dd9510ad47ed34.png](https://img-blog.csdnimg.cn/img_convert/0a8d17bd7ebcf44276dd9510ad47ed34.png)
如果你的实现类有上面的注解可以不声明 jdbcType 和 javaType 属性。
如果是javaConfig 方式 ,第一你可以通过SqlSessionFactory对象取到Configuration对象将typeHandler注册进去。
如果你使用mybatis-spring组件,可以在SqlSessionFactoryBean的setTypeHandlersPackage方法中配置typeHandler的集中包路径,那么框架将会自动扫描并注册他们。
springboot中对应的配置属性是mybatis.typeHandlersPackage。这样你在映射xml配置文件中只需要声明jdbcType和javaType,无需再声明具体的typeHandler。
3.总结
今天我们学习了mybatis开发中如何通过使用类型处理器进行类型的转换处理,如何处理枚举,如何自定义处理器并使用它。相信对你在java开发过程中会有很大的帮助。’
![0100801ce4655b1c783eb6be7553494d.png](https://img-blog.csdnimg.cn/img_convert/0100801ce4655b1c783eb6be7553494d.png)