不管是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,仍是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。java
下面是常见的一些对应类型:app
以BigDecimalTypeHandler看一下,它主要完成了哪些工做。函数
这个类的第一个方法是对预处理语句(PreparedStatement)设置参数,以后的三个函数都是从ResultSet或者用于执行存储过程的CallableStatement语句中获取BigDecimal类型的数值,用于向BigDecimal类型的Java字段赋值。
BigDecimalTypeHandler继承的BaseTypeHandler是个泛型类,其余的TypeHandler也是经过继承这个抽象类,实现其中的抽象方法,实现类型转换的工做。工具
这个抽象类实现了TypeHandler接口,这个接口主要定义了类型转换的几种操做。ui
至于这个抽象类继承的TypeReference<T>,主要是提供了获取这个T具体是哪一个类型。在判断使用使用哪一个TypeHandler时有用,后文会看到。spa
大体介绍了TypeHandler的做用,及其相关类,咱们来看看如何使用它。
今天遇到的主要是从SqlServer中取数据,遇到不少列都是Numeric(10,2)类型,指的是字段是数字型,长度为10,小数为两位。Mybatis默认的BigDecimalTypeHandler取到后,都默认变成4位小数,不够的补了0。而上层的要求是,拿到的和数字相关的数据都要2位小数。3d
有两种作法,一种是在全部给上层赋值的时候,都人工对BigDeciam的数据作以下操做。code
setScale(2, BigDecimal.ROUND_HALF_UP)
由于这是一个全局性的要求,全部相关的地方,都须要有这个代码,虽然能够写一个工具类,各个地方调用,但就对本来间接的代码形成了侵入。既然这样,为何不试试TypeHandler。xml
个人作法是继承BigDecimalTypeHandler,覆盖原来的取值方法,对取到的数值作范围限定。对象
加上@MappedJdbcTypes注解是为了代表这个类是用于映射JdbcType的NUMERIC类型,这会覆盖默认的用于转换Java BegDecimal和Jdbc NUMERIC的BigDecimal,在后面源码中可略窥一二。
开发完这个转换类后,你须要在Mybatis的配置文件中声明这个TypeHandler,这样Mybatis才知道你本身声明了一个TypeHandler。
<typeHandlers> <typeHandler handler="com.codelab.learn.SubBigDecimalTypeHandler"/> </typeHandlers>
这样TypeHandler就起做用了。下面是先后效果。
首先Mybatis有一个默认的TypeHandler实现,这些TypeHandler是如何被Mybatis识别的呢。
答案是TypeHandlerRegistry。在Mybatis初始化配置的时候,TypeHandlerRegistry会把JdbcType和Java类型对应的映射关系注册进该类内部的Map中。
JDBC_TYPE_HANDLER_MAP中记录的是JdbcType和TypeHandler对应的关系。
TYPE_HANDLER_MAP中记录的是Java类型和对应的全部JdbcType以及其对应TypeHandler的映射关系关系。
UNKNOWN_TYPE_HANDLER是在执行BaseTypeHandler的抽象方法时,去先解析出来该用什么TypeHandler,目前还没用到,先不研究。
ALL_TYPE_HANDLERS_MAP中记录的是全部TypeHandler的Class和其实例之间的映射关系。
咱们以系统默认注册的三个做为例子,看看整个执行的流程
1 register(String.class, new StringTypeHandler()); 2 register(String.class, JdbcType.NCHAR, new NStringTypeHandler()); 3 register(JdbcType.NCHAR, new NStringTypeHandler());
第一个是告诉 String类型的转换,要用StringTypeHandler。
直接进的这个函数,由于咱们的TypeHandler上并无打注解,所以直接进入箭头标记的逻辑。
而后继续注册,只不过jdbcType是null。
后续的代码比较简单,会先从Type_HANLER_MAP中看是否有已经存在的 Map<JdbcType, TypeHandler<?>> map,没有的话,新建,而且放入TYPE_HANDLER_MAP中,在Map<JdbcType, TypeHandler<?>> map放入这次的jdbcType和它对应的Handler。最后在ALL_TYPE_HANDLERS_MAP放入handler的类和实例。
第二个,传入了jdbcType是NCHAR,和第一个相似,但直接就进入了最后一步的注册环节,没有去判断传入什么样的jdbcType类型,由于已经指定了。
第三个是绑定了 jdbcType和Handler之间的对应关系。
OK,前面是系统默认注册进去的,那咱们看一下咱们在如何使用章节中添加进去的SubBigDecimalTypeHandler是如何被注册进去的呢。
Mybatis在应用中启动时,会根据XML文件初始化配置,负责解析XML生成配置类的就是XMLConfigBuilder,经过调用其中的parseConfiguration方法填充配置类。
箭头表示处,就是解析typehandlers节点,咱们看看他具体作了些什么。
由于咱们不是对整个package进行注册,因此进入else分支,由于只代表了一个最简单的Handler,因此要获取的字段都为null,由此咱们也能够看出,在编写XML时,咱们也是能够直接指定映射关系的,由于获取不到javaType和jdbcType,后面应该是会根据这个类再解析一波。跟注册相关的又回到了TypeHandlerRegistry这个类里面,职责仍是很清晰的。
在这个方法里面,首先会获取有没有打MappedType这个注解,这个注解是代表这个类对应处理的JavaType是啥。咱们这边没有找到,所以继续往下走。
从Mybatis3.1.0开始,会自动解析这个类对应的Java类型,还记得以前咱们继承的BigDecimalTypeHandler中咱们的基类BaseTypeHandler继承了TypeReference么?
这个类的构造函数会获取泛型中具体的类型是什么,细节代码能够私下看一下。
获取到了具体的Java类型,咱们就继续往下传。
由于咱们的subBigDecimalTypeHandler是打了MappedJdbcType注解的,所以以后的步骤和register(String.class, JdbcType.NCHAR, new NStringTypeHandler())是一致的,能够回看上文。
到这里,TypeHandler的注册部分已经完成了。
在以前的关于映射的文章中,咱们提过,Mybatis完成映射后,会选择合适的TypeHandler处理器,完成对Java业务对象的赋值,咱们首先找到入口在哪里。
完成赋值的就是在1,2处,咱们这边用的是自动映射,所以进1看看,具体关于TypeHandler的处理,不会有太大的差别。
在以前的createAutomaticMappings,找到列名后,会找出对应的字段,首先会判断是否有对应的TypeHandler。
由于你知道了JDBC的类型,也经过反射知道了Java的类型。
这边就首先去TYPE_HANDLER_MAP中找已经存在的JDBC-TypeHandler的映射,若是有的话直接取,没有的话,就默认取null所对应的那个类型。
由于咱们知道jdbc的类型是NUMERIC,并且以前注册的SubBigDecimalTypeHandler对应的JDBC类型是NUMERIC。
所以就取了更匹配的SubBigDecimalTypeHandler。
以后就是调用getResult方法,完成值的获取便可。
本文主要介绍了
但愿获得您的点赞,打赏支持,谢谢。