Mybatis源代码分析之类型转换

Mybatis源代码分析之类型转换
代长亚ORM框架最重要功能是将面向对象方法中的对象和关系型数据库中的表关联了起来,在关联过程中就必然涉及到对象中的数据类型和数据库中的表字段类型的转换,Mybatis中的org.apache.ibatis.type包主要就是实现这个功能。
一、org.apache.ibatis.type的基础类
在mybatis的官网中(http://mybatis.github.io/mybatis-3/configuration.html#typeHandlers)关于类型转换有如下的描述
Whenever MyBatis sets a parameter on a PreparedStatement or retrieves a value from a ResultSet, a TypeHandler is used to retrieve the value in a means appropriate to the Java type.
当MyBatis为PreparedStatement 设置参数时或者从ResultSet中获取数据时,会根据Java类型使用TypeHandler 去获取相应的值。
官网中也列出了每一个TypeHandler用来处理对应的JDBC类型和JAVA类型。
1、TypeHandler接口
这个接口有三个方法,一个set,用来给PreparedStatement对象对应的列设置参数;两个get,从ResultSet和CallableStatement获取对应列的值,不同之处是一个是取第几个位置的值,一个是取具体列名所对应的值。set用来将Java对象中的数据类型转换为JDBC中对应的数据类型,get用来将JDBC中对应的数据类型转换为Java对象中的数据类型转换。
2、BaseTypeHandler抽象类
在进行软件设计时提倡面向接口的设计,但接口只是一个接口,并不做任何实质性的操作,还需有一系列的实现才可以真正的达到目标。BaseTypeHandler类便是对TypeHandler接口的初步实现,在实现TypeHandler接口的三个函数外,又引入了3个抽象函数用于null值的处理。
3、DateTypeHandler
书接上文,BaseTypeHandler类也是一个抽象类,按照Java的规定抽象类并不能初始化,也不能直接使用,因而还需要有具体的类。在type包中有十多个具体的类来具体处理类型转换,每一个类处理一个数据类型,像long、int、double等等,我们以一个稍微复杂些的DateTypeHandler类为例,了解下对日期是如何进行处理的。
1)setNonNullParameter
1 | public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) |
首先将参数parameter这个Object转换为Date类型,而后通过Date对象的getTime()将日期转为毫秒数,而后再将毫秒数转换为java.sql.Timestamp对象。即将java.util.Date对象转换为java.sql.Timestamp对象。
2)getNullableResult
1 | public Object getNullableResult(ResultSet rs, String columnName) |
从上面的代码可以看出这两个函数的作用就是将 java.sql.Timestamp对象转换为 java.util.Date对象.
4、类图
综合而言,type包基础类的类图示例如下:
二、自定义类型处理或覆盖默认的类型处理
在mybatis的官网中(http://mybatis.github.io/mybatis-3/configuration.html#typeHandlers)关于有如下的描述:
You can override the type handlers or create your own to deal with unsupported or non-standard types. To do so, simply extend theorg.apache.ibatis.type.BaseTypeHandler class and optionally map your new TypeHandler class to a JDBC type.
可以覆盖type handler或者创建自己的type handler去处理mybatis不支持的或者非标准的数据类型。实现这个功能,只需要继承org.apache.ibatis.type.BaseTypeHandler类然后选择新的TypeHandler类和JDBC类型的对应关系即可。
在官网中也给出了详细了示例,这里不再进行重复。其中用到了两个注解:MappedJdbcTypes 和 MappedTypes,我们可以看下这两个注解的定义文件:
1 | @Retention(RetentionPolicy.RUNTIME) |
MappedJdbcTypes传入的是JdbcType类型的(JdbcType是type包中的一个枚举类型),MappedTypes传入的Class类型。
在声明完自己的类型转换之后,还需要让mybatis知道这些新的类型转换类,这可以通过在配置文件中添加typeHandlers节点来实现。可以添加一个类,也可以添加一个包中所有的类。
1 | <typeHandlers> |
三、typeHandler的注册:TypeHandlerRegistry
前面介绍了mybatis中已定义好的typeHandler,也介绍了如何自定义typehandler,以及如何让mybatis知道这次自定义的typehandler。现在剩下最关键的步骤,在mybatis初始化后如何将这些typehandler注册到mybatis中,在执行数据库操作去使用这些类。这些操作是由TypeHandlerRegistry类实施的。
我们已经知道,mybatis在使用时需要一个配置文件来进行各种各样的设置,与这个配置文件相对应的是org.apache.ibatis.session.Configuration这个类,配置文件中每一项都对应Configuration类中的一个属性,typehand就是Configuration类中的一个属性。
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
我们现在来看看TypeHandlerRegistry类中常用的方法及其作用。
1、TypeHandlerRegistry类中的属性和常用方法
1 | private static final Map<Class<?>, Class<?>> reversePrimitiveMap = new HashMap<Class<?>, Class<?>>() { |
1)reversePrimitiveMap
可以看出来,这个map就是一个java中的基本数据类型和他们对应的类一一关联起来,像Byte和byte。在进行注册时会用到这个属性,详见第三小节。
2)JDBC_TYPE_HANDLER_MAP
JdbcType和typehandler的对应关系,通过如下的函数进行维护
1 | public void register(JdbcType jdbcType, TypeHandler handler) { |
3)TYPE_HANDLER_MAP
TYPE_HANDLER_MAP属相是一个关键的属性,java类型和jdbctype的对应关系及处理类都存在这个属性中,从这个map的定义中可以看到多个jdbc类型能够对应到一个java类型。通过如下的函数进行维护:
1 | public void register(Class<?> type, JdbcType jdbcType, TypeHandler handler) { |
4)注册自定义type handler
在注册自定义的type handler之前需要先定位到具体的类或者包,类的处理比较简单,直接利用java的反射机制就可以知道这个类的Class属性了。对于包就稍微复杂些,在mybatis中是利用io包ResolverUtil类中的find函数来实现的,这里不做详细介绍,等介绍到mybatis的io包时再详细说明。我们来看注册包的函数:
1 | public void register(String packageName) { |
从上面的代码中可以看到,真正进行注册用到的是如下的函数,先处理MappedTypes这个注解:
1 | public void register(TypeHandler handler) { |
上面的函数又调用了如下的函数,去处理MappedJdbcTypes注解:
1 | public void register(Class<?> type, TypeHandler handler) { |
最后调用第三小节中提到的函数进行注册到map中,供程序执行时调用。
四、TypeHandlerRegistry的初始化
前面介绍了如何自定义类型转换处理类并注册到mybatis中,那type包中有不少mybatis已实现的类型转换处理类,这些类是如何及在何时注册到mybatis中的呢?
mybatis中的实现很简单,它把这些都放在了TypeHandlerRegistry的构造函数中了。由于构造函数比较大,也没有用到什么新方法,再此就不贴代码了,有兴趣的读者可以自己去观看。
五、小结
本文对Mybatis中的type转换做了介绍,介绍了其设计结构、常用的方法、如何自定义类型处理类、如何进行注册等内容,希望对大家理解mybatis如何实现java类型到JDBC类型转换有所帮助。
当然,这里这是介绍了如何转换,但是这些转换是怎么使用的并没有涉及,这些内容将放到mybatis的executor中进行介绍。