MyBatis作为一个ORM框架,在实现对象到关系数据库映射的过程中,一个无法避免的问题就是Java类型和JDBC类型之间的相互转换,而TypeHandler的作用就在于此,其作用是实现Java类型向JDBC类型之间的转换。
从上面的描述上来看,TypeHandler的作用对象是一个对象属性,从Java类型向JDBC类型之间的转换这样的说法或许过于抽象,举个例子来说,比如我们有个对象的属性是String数组,我想在插入数据库的时候将这个属性在插入数据库的时候是以一个varchar的属性记录,数组中每个元素以逗号相隔,这是从Java类型转换到JDBC;而从数据库获取这个对象的属性的时候又能够分割开逗号返回对象String数组,这是从JDBC转换到Java类型。
从实现层次上来说TypeHandler的功能,就是在我们使用MyBatis插入一个对象数据的时候,对象的每条属性是在经过如何处理后放入JDBC的SQL语句中,在取出一条数据库记录的时候,每一列的数据又是经过如何的处理放入对象的属性中的,这两个方面我们完全可以从MyBatis提供TypeHandler
类中得以体现。
org.apache.ibatis.type.TypeHandler
是所有TypeHandler的基类,里面有四个方法
其中setParameter是将一个对象的属性经过转换后插入到PreparedStatement,后面三个方法是将从ResultSet中取出的值处理后返回给对象的属性(分别是通过列名,列索引来获取值,最后一个适用于存储过程)。而org.apache.ibatis.type.BaseTypeHandler
则实现了TypeHandler,做了一些null值上的处理,一般情况下我们自定义的类型处理器直接继承自BaseTypeHandler,并覆盖一下四个方法
对于一下简单的TypeHandler MyBatis已经默认提供了,对于一些比较难处理的,比如数组,容器,自定义简单对象需要我们自己自定义TypeHandler进行类型处理。
自定义StringArrayTypeHandler
上图显示了在构建MyBatis程序的一般过程,其中橙色的线是我们在使用TypeHandler的过程中的一些重要步骤,我在图中做了数字标注。
编写StringArrayTypeHandler
首先第一步是编写属性的类型转换器,我们这里用编写的String数组的类型转换器作为例子。假设JavaBean是Student有如下属性
其中interests属性是一个数组,我们第一步编写StringArrayTypeHandler,它继承自BaseTypeHandler
编写Mapper接口
第二步是先写业务需求的接口,我们这里定义了插入和查找两个接口
配置Mapper文件
定义完业务接口就可以配置Mapper文件,这个是我们使用TypeHandler的地方,有两处使用到了我们定义的StringArrayTypeHandler,第一处是在我们插入值的参数处,使用了typeHandler参数。
另一处是在select的resultMap中
这两个方法Mapper方法的调用会引起我们自定义的StringArrayTypeHandler的setNonNullParameter和getNullableResult方法的调用。
Mapper配置文件和Mapper接口直接的管理是Mapper配置文件来维护的,Mapper配置文件namespace指定了Mapper接口的全称限名,并且对应的select,insert子标签的id对应于Mapper接口的方法名。
基础配置文件加载Mapper文件
第四步就是在MyBatis的基础配置文件中加载Mapper配置文件
如果是非显示使用TypeHandler还要在基础配置文件中注册TypeHandler,显示使用则不用。显示使用是指直接指定TypeHandler的全称限名,非显示使用只指定type和jdbcType,让框架去寻找合适的TypeHandler
使用Mapper接口进行测试
这一阶段需要经过加载基础配置文件,获取SqlSessionFactory,打开SqlSession,getMapper执行sql语句的过程,至此整个过程结束,程序顺利运行
遇到的问题
- 没有声明别名的类一定要写成全称限名
- 主键回填不仅要设置useGeneratedKeys=true,还要设置keyProperty
EnumOrdinalTypeHandler和EnumTypeHandler
这两个都是MyBatis内置的处理Enum类型属性的类型转换器,其区别在于在存入数据库的时候EnumOrdinalTypeHandler是调用ordinal()方法获取索引存储,而EnumTypeHandler是调用name()方法获取名称进行存储,取回的时候也是同样策略的逆过程,比如我们上面使用了EnumOrdinalTypeHandler,其对应的枚举类型如下,而数据库存储的stu_sex为对应的索引0或1