MyBatis在金融领域的使用非常广泛,作为一款优秀的ORM框架,其独特的优势在于:
①对于sql的分离,使业务开发同事更专注于逻辑实现;
②MyBatis对存储过程的有很好的支持(特别是对一些时间延时要求高,业务较为稳定的场景)。
作为一款ORM框架,不同的系统之间数据类型的转换就是首先要考虑的;MyBatis一项基本的功能,就是帮助我们,将JDBC类型与Java类型之间转换变得透明,能解放大家更多得精力去与产品吵架(狗头)。开个玩笑,正是由于JDBC类型与Java类型,并不是一对一得关系,所以我们更加需要MyBatis去帮我们解决这类繁琐而且重复的工作。
TypeHandler,就是MyBatis给出的解决方案。
TypeHandler简述
MyBatis在设置预处理语句(PreparedStatement)中的参数,或从结果集中取出一个值时, 会选择使用对应的TypeHandler类型处理器,将获取到的值以合适的方式转换成 Java 类型;MyBatis的在内部设定了很多基础的处理器,下图是从MyBatis代码中截取的,框架内部封装好的类型处理器。
TypeHandler使用
在MyBatis框架中,为了方便开发人员,提供了三种方式使用TypeHandler:
- 从config文件中,通过typeHandlers以及子标签typeHandler,对自定义的TypeHandler进行注册;
- 或者通过package子标签,对TypeHandler所在包进行扫描注册;
- 在mapper.xml文件中,在resultMap或者在参数使用中,可以显示的声明TypeHandler,如下:
1 | bash复制代码<result column="phone" property="phone" |
- spring boot集成了MyBatis的starter,可以通过*.properties的文件进行配置
1 | ini复制代码mybatis.type-handlers-package=${packagePath} |
PS:笔者并未找到可以通过单个注册的处理器的方法
TypeHandler原理
那么TypeHandler在整个生命周期中是如何加载及使用的呢?
首先是注册阶段,MyBatis会通过XMLConfigBuilder
对配置的xml文件中的标签进行解析,其中就包含上文中提到的TypeHandler相关标签;在解析时,会调用TypeHandlerRegistry
的注册方法,将自定义的handler加载到JVM中;如果大家有兴趣,点开这个注册类,就能发现它的构建方法中,将上文列举的一些MyBatis框架内部定义的基础handler,进行了统一的初始化,并用Map将其与JDBC的类型建立的关联;
接下来,在解析mapper文件时,XMLMapperBuilder
会选择适当的处理器;在解析ResultMap和ParameterMap时,从上阶段注册的TypeHandler中,找到最合适的TypeHandler,或者从mapper文件中,读取显示声明的TypehHandler,存入ParameterMapping
实例中;
最终语句执行阶段,会调用处理器,对参数或查询结果进行转换;对应的Sql语句在输入参数时,调用TypeHander接口的setParameter
方法,进行入参转换操作;获取到查询结果后,会调用getResult
的方法,对结果转换从而返回。
自己定义一个TypeHandler
用两个实际的例子,来分享下我的使用场景。
场景1,用户更新标识;
要求:公司内部有单独的人力系统维护人员信息,笔者所在系统的人员信息修改有三种方式:1、员工手动修改;2、管理员手动修改;3、来自行内人力资源系统信息。业务规定员工手动修改信息后,不再同步来自人力资源系统的修改信息;同时需要根据修改的信息,设置同步标志,每一种不同的信息,必须通过不同的标识位进行控制。
设计与实现:人员主表增加更新标识位字段,通过二进制位,标识每个不同种类的信息的是否同步;代码中增加专用的DTO类,表明该数据为特殊更新属性标识;新增注解和二进制位枚举,通过在DTO类属性添加注解,指定该属性对应的枚举信息;增加专用TypeHandler用于数据库中的number类型与DTO类型的转换;在mapper文件中,显示的指定具体的TypeHandler。
Talk is cheap, show me your code.
注解类:
1 | less复制代码@Target(ElementType.FIELD) |
DTO类:
1 | ini复制代码public class PropertiesUpdateMarkDTO implements Serializable { |
Enum类:
1 | csharp复制代码public enum PersonDetailChangeEnum { |
TypeHandler实现:
1 | java复制代码@MappedTypes(PropertiesUpdateMarkDTO.class) |
mapper.xml文件:
1 | ini复制代码<resultMap id="HrsPersonVOResultMap" type="*.person.bo.PersonVO" extends="PersonVOResultMap"> |
场景2,银行卡号脱敏展示;
要求:用于展示用户银行卡账号的页面,为了保证客户信息不被泄露,都必须经过脱敏处理的,显示带有*的银行卡号;
设计与实现:增加特殊的String字段的TypeHandler类;在mapper中,找到需要脱敏的DO中字段,同时在对应的ResultMap中,显示的指定该TypeHandler;数据库读取到的银行卡账号信息,通过getNullableResult
方法,进行转换,从而达到脱敏处理。
TypeHandler实现:
1 | java复制代码public class DesCardNoTypeHandler extends BaseTypeHandler<String> { |
ResultMap信息:
1 | ini复制代码<resultMap id="BaseUserCardInfo" type="com.example.entity.card.UserCard"> |
运行结果:
1 | ini复制代码Created connection 2006112337. |
本文转载自: 掘金