文档章节

[Mybatis] TypeHandler的简单应用及源码分析

kailuncen
 kailuncen
发布于 2017/06/03 21:42
字数 1841
阅读 824
收藏 35
点赞 0
评论 9

TypeHandlers

无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。

下面是常见的一些对应类型: 对应类型示例

以BigDecimalTypeHandler看一下,它主要完成了哪些工作。 BigDecimalTypeHandler

这个类的第一个方法是对预处理语句(PreparedStatement)设置参数,之后的三个函数都是从ResultSet或者用于执行存储过程的CallableStatement语句中获取BigDecimal类型的数值,用于向BigDecimal类型的Java字段赋值。 BigDecimalTypeHandler继承的BaseTypeHandler是个泛型类,其他的TypeHandler也是通过继承这个抽象类,实现其中的抽象方法,实现类型转换的工作。 输入图片说明

这个抽象类实现了TypeHandler接口,这个接口主要定义了类型转换的几种操作。 输入图片说明

至于这个抽象类继承的TypeReference<T>,主要是提供了获取这个T具体是哪个类型。在判断使用使用哪个TypeHandler时有用,后文会看到。 输入图片说明


如何使用

大致介绍了TypeHandler的作用,及其相关类,我们来看看如何使用它。 今天遇到的主要是从SqlServer中取数据,遇到很多列都是Numeric(10,2)类型,指的是字段是数字型,长度为10,小数为两位。Mybatis默认的BigDecimalTypeHandler取到后,都默认变成4位小数,不够的补了0。而上层的要求是,拿到的和数字相关的数据都要2位小数。

有两种做法,一种是在所有给上层赋值的时候,都人工对BigDeciam的数据做如下操作。

setScale(2, BigDecimal.ROUND_HALF_UP)

因为这是一个全局性的要求,所有相关的地方,都需要有这个代码,虽然可以写一个工具类,各个地方调用,但就对原本间接的代码造成了侵入。既然这样,为什么不试试TypeHandler。

我的做法是继承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对应的关系。 12 TYPE_HANDLER_MAP中记录的是Java类型和对应的所有JdbcType以及其对应TypeHandler的映射关系关系。 13 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方法,完成值的获取即可。


总结

本文主要介绍了

  1. 什么是TypeHandler。
  2. 如何使用TypeHandler。
  3. 从系统默认的以及自定义的TypeHandler的注册和获取的角度,从源码层面分析了整个过程。

希望得到您的点赞,打赏支持,谢谢。 输入图片说明

© 著作权归作者所有

共有 人打赏支持
kailuncen
粉丝 83
博文 17
码字总数 28778
作品 0
卢湾
后端工程师
加载中

评论(9)

kailuncen
kailuncen
请多多指教
kailuncen
kailuncen

引用来自“KKiCC”的评论

请教一下,我返回值类型是一个pojo,里面有一个字段是list<string> 数据库里面的字段直是 1,2,3,怎么装让list<string> =new arraylist(1,2,3)
[TestPO{id=1, content=[1, 2, 3]}, TestPO{id=2, content=[4, 5, 6]}]
最后效果
kailuncen
kailuncen

引用来自“KKiCC”的评论

请教一下,我返回值类型是一个pojo,里面有一个字段是list<string> 数据库里面的字段直是 1,2,3,怎么装让list<string> =new arraylist(1,2,3)
在resultMap的那一列,指定他们的typehandler
result column="content" property="content" jdbcType="VARCHAR" javaType="java.util.ArrayList" typeHandler="typehandler.StringListTypeHandler"

如果对您有帮助的话,请对我的博客点赞转发,谢谢。
kailuncen
kailuncen

引用来自“KKiCC”的评论

请教一下,我返回值类型是一个pojo,里面有一个字段是list<string> 数据库里面的字段直是 1,2,3,怎么装让list<string> =new arraylist(1,2,3)
回答您的问题,因为Mybatis默认是不支持 VARCHAT -> ARRAYLIST之间的转换的。所以您需要在指定一个新的TypeHandler。

@MappedJdbcTypes(JdbcType.VARCHAR)
public class StringListTypeHandler extends BaseTypeHandler> {

public void setNonNullParameter(PreparedStatement ps, int i, List parameter, JdbcType jdbcType) throws SQLException {

}
// 如果只是读取处理的话,就在这里处理吧
public List getNullableResult(ResultSet rs, String columnName) throws SQLException {
List result = new ArrayList();
String[] content = rs.getString(columnName).split(",");
for (String item : content) {
result.add(item);
}
return result;
}

public List getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return null;
}

public List getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return null;
}
}

然后在您的resultMap中,针对那一列,指定这个typehandler。
kailuncen
kailuncen

引用来自“KKiCC”的评论

请教一下,我返回值类型是一个pojo,里面有一个字段是list<string> 数据库里面的字段直是 1,2,3,怎么装让list<string> =new arraylist(1,2,3)
应该是可以做的,实践后附上相关说明
KKiCC
KKiCC
请教一下,我返回值类型是一个pojo,里面有一个字段是list<string> 数据库里面的字段直是 1,2,3,怎么装让list<string> =new arraylist(1,2,3)
J-Fla
J-Fla
JSONObjectHandler,JSONArrayHandler
J-Fla
J-Fla
JSONObjectHandler,JSONArrayHandler
kailuncen
kailuncen
叮叮咚咚
mybatis(五)TypeHandler简介及配置(mybatis源码篇)

上篇文章为大家介绍了mybatis中别名的使用,以及其源码。本篇将为大家介绍TypeHandler, 并简单分析其源码。 Mybatis中的TypeHandler是什么?   无论是 MyBatis 在预处理语句(PreparedSta...

LCZ777 ⋅ 2014/12/19 ⋅ 0

mybatis源码分析之TypeHandler

上一篇: mybatis源码分析之MapperMethod https://my.oschina.net/u/657390/blog/755787 分析了MapperMethod从创建到执行的过程,MapperMethod的执行包括执行sql返回结果. 在执行sql和返回结果...

udbwcso ⋅ 2016/11/18 ⋅ 0

【深入浅出MyBatis系列二】配置简介(MyBatis源码篇)

深入浅出MyBatis系列 【深入浅出MyBatis系列一】MyBatis入门 【深入浅出MyBatis系列二】配置简介(MyBatis源码篇) 【深入浅出MyBatis系列三】Mapper映射文件配置 【深入浅出MyBatis系列四】...

陶邦仁 ⋅ 2015/12/22 ⋅ 3

Mybatis3.3.x技术内幕(十二):Mybatis之TypeHandler

Mybatis中的TypeHandler有两个功能,一个是完成javaType至jdbcType的转换,另外一个是完成jdbcType至javaType的转换。 public interface TypeHandler<T> { void setParameter(PreparedStatem......

祖大俊 ⋅ 2016/05/06 ⋅ 0

mybatis(六)objectFactory、plugins、mappers简介与配置

上篇文章简单看了一下TypeHandler, 本次将结束对于mybatis的配置文件的学习, 本次涉及到剩下没提及到的几个节点的配置:objectFactory、databaseIdProvider、plugins、mappers。 那么,接下...

LCZ777 ⋅ 2014/12/19 ⋅ 0

Mybatis 学习系列文章

深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇) 深入浅出Mybatis系列(九)---强大的动态SQL 南轲梦 2014-11-12 16:47 深入浅出Mybatis系列(八)---mapper映射文件配置之select、r...

boonya ⋅ 2015/07/13 ⋅ 1

Mybatis3.3.x技术内幕(十):Mybatis初始化流程(下)

Mybatis初始化过程中,解析parameterMap、resultMap、"select|insert|update|delete"元素,无疑是重头戏。本节将详细分析解析过程。 元素parameterMap将会解析为ParameterMap对象,该对象包含...

祖大俊 ⋅ 2016/05/04 ⋅ 0

【深入浅出MyBatis系列五】SQL执行流程分析(源码篇)

深入浅出MyBatis系列 【深入浅出MyBatis系列一】MyBatis入门 【深入浅出MyBatis系列二】配置简介(MyBatis源码篇) 【深入浅出MyBatis系列三】Mapper映射文件配置 【深入浅出MyBatis系列四】...

陶邦仁 ⋅ 2015/12/22 ⋅ 0

利用st_geometry进行图形叠加分析--结合mybatis

在上一遍利用stgeometry进行图形叠加分析文章的基础上,结合mybatis操作stgeometry,主要简单实现一下三个功能:利用st_geometry两个表之间图形表的叠加分析、WKT与图形表的叠加分析、将WKT...

翻 ⋅ 2016/11/21 ⋅ 0

MyBatis整合Spring的实现(11)

前一章节已经介绍了,把方法分解成什么样子来分析,这里先来分析一个方法resultMapElements,在这个方法前还有cacheRefElement与cacheElement,但是由于配置文件中没有配置这2项,所以这里就...

tara_qri ⋅ 2015/10/26 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

JDK1.6和JDK1.7中,Collections.sort的区别,

背景 最近,项目正在集成测试阶段,项目在服务器上运行了一段时间,点击表格的列进行排序的时候,有的列排序正常,有的列在排序的时候,在后台会抛出如下异常,查询到不到数据,而且在另外一...

tsmyk0715 ⋅ 32分钟前 ⋅ 0

spring RESTful

spring RESTful官方文档:http://spring.io/guides/gs/rest-service/ 1. 可以这么去理解RESTful:其实就是web对外提供的一种基于URL、URI的资源供给服务。不是一个原理性知识点。是一个方法论...

BobwithB ⋅ 33分钟前 ⋅ 0

C++ 中命名空间的 5 个常见用法

相信小伙伴们对C++已经非常熟悉,但是对命名空间经常使用到的地方还不是很明白,这篇文章就针对命名空间这一块做了一个叙述。 命名空间在1995年被引入到 c++ 标准中,通常是这样定义的: 命名...

柳猫 ⋅ 36分钟前 ⋅ 0

@Conditional派生注解

@Conditional派生注解(Spring注解版原生的@Conditional作用) 作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效; @Conditional扩展注解 作用(判...

小致dad ⋅ 37分钟前 ⋅ 0

适配器模式

适配器模式 对象适配器 通过私有属性来实现的类适配器 通过继承来实现的接口适配器 通过继承一个默认实现的类实现的

Cobbage ⋅ 40分钟前 ⋅ 0

Java 限流策略

概要 在大数据量高并发访问时,经常会出现服务或接口面对暴涨的请求而不可用的情况,甚至引发连锁反映导致整个系统崩溃。此时你需要使用的技术手段之一就是限流,当请求达到一定的并发数或速...

轨迹_ ⋅ 44分钟前 ⋅ 0

GridView和子View之间的间隙

默认的情况下GridView和子View之间会有一个间隙,原因是GridView为了在子View被选中时在子View周围显示一个框。去掉的办法如下: android:listSelector="#0000" 或 setSelector(new ColorDra...

国仔饼 ⋅ 48分钟前 ⋅ 0

idea插件开发

1 刷新页面要使用多线程 2 调试要使用restart bug 不要去关闭调试的idea 否则再次启动会卡住

林伟琨 ⋅ 48分钟前 ⋅ 0

Java 内存模型

物理机并发处理方案 绝大多数计算任务,并不是单纯依赖 cpu 的计算完成,不可避免需要与内存交互,获取数据。内存要拿到数据,需要和硬盘发生 I/O 操作。计算机存储设备与 cpu 之间的处理速度...

长安一梦 ⋅ 54分钟前 ⋅ 0

思路分析 如何通过反射 给 bean entity 对象 的List 集合属性赋值?

其实 这块 大家 去 看 springmvc 源码 肯定可以找到实现办法。 因为 spirngmvc 的方法 是可以 为 对象 参数里面的 list 属性赋值的。 我也没有看 具体的 mvc 源码实现,我这里只是 写一个 简...

之渊 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部