MyBatis源码解析(十)——Type类型模块之类型处理器TypeHandler

原创作品,可以转载,但是要标注出处地址:http://www.cnblogs.com/V1haoge/p/6715063.html

1、回顾

  之前的星星点点首分别解析了档次别名注册器和种类处理器注册器,此双方都是解析XML映射文件中参数类型和归结果类型的基本功,别名注册器用于通过别名找到呼应的类类型,类型处理器注册器则用来通过类类型来找到相应了种处理器和数据库类型,以之来就进出数据库数据以及java之间种的换。

  我们以路处理器注册器一首被一度略介绍了花色处理器,那便是用于java类型与数据库类型中开展映射处理的工具类,这同一篇中只要详细剖析一下MyBatis中的品种处理器。

2、类型处理器

2.1 类架构

  图片 1

  从者的希冀被好见到MyBatis中全项目处理器实现架构,TypeHandler接口定义了色处理器,而TypeReference抽象类则定义了一个色引用,用于引用一个泛型类型(此处很肤浅,不好理解,详见后续解析),BaseTypeHandler则是种处理器的基本功,是兼具品类处理器的公家模块,几乎有的花色处理器都是透过直接接轨BaseTypeHandler来实现的。

2.2 类型处理器接口:TypeHandler

  TypeHandler是用来定义类型处理器的接口,内部生是略:

 1 package org.apache.ibatis.type;
 2 import java.sql.CallableStatement;
 3 import java.sql.PreparedStatement;
 4 import java.sql.ResultSet;
 5 import java.sql.SQLException;
 6 /**
 7  * 类型处理器
 8  * 
 9  */
10 public interface TypeHandler<T> {
11 
12   //设置参数
13   void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
14 
15   //取得结果,供普通select用
16   T getResult(ResultSet rs, String columnName) throws SQLException;
17 
18   //取得结果,供普通select用
19   T getResult(ResultSet rs, int columnIndex) throws SQLException;
20 
21   //取得结果,供SP用
22   T getResult(CallableStatement cs, int columnIndex) throws SQLException;
23 
24 }

  通过上述源码可以看出这接口中定义了种处理器基本的季个措施,其中分为两那个接近,第一好像是设置参数的方式setParameter(),这个方法是用于安装数据库操作的参数,例如查询参数、删除参数、更新参数等;另一样近乎是用于取结果的主意,这无异接近方式以细分为简单好种,第一种植是由结果集中取结果,按照获取之法分为两栽:一种植是由此列名(columnName)来取得,另一样种是经过列下标(columnIndex)来得到,这有限种植获得方式正对诺我们一直行使JDBC进行数据库查询结果负获取数据的一定量种植办法,第二种植是本着存储过程而设,通过列下标的法子来取存储过程输出结果遭到的数量。

  总的来说类型处理器就是是零星方的打算,一方面用Java类型的参数(T
prarameter)设置及数据库操作脚本中(匹配数据库类型jdbcType),另一样种植是收获操作结果及Java类型(T)中。

2.3 类型引用:TypeReference

  这个路引用的企图适用于得原生类型,Java中的原生类型又曰基本项目,即byte、short、int、long、float、double、boolean、char八大基本数据列。

  这个仿佛有必不可少重点教学一下,同时为是以提高一下Java中列的概念,来拘禁源码:

 1 package org.apache.ibatis.type;
 2 
 3 import java.lang.reflect.ParameterizedType;
 4 import java.lang.reflect.Type;
 5 
 6 /**
 7  * References a generic type.
 8  *
 9  * @param <T> the referenced type
10  * @author Simone Tripodi
11  * @since 3.1.0
12  * 3.1新加的类型引用,为了引用一个泛型类型
13  */
14 public abstract class TypeReference<T> {
15 
16   //引用的原生类型
17   private final Type rawType;
18 
19   protected TypeReference() {
20     rawType = getSuperclassTypeParameter(getClass());
21   }
22 
23   Type getSuperclassTypeParameter(Class<?> clazz) {
24     //得到泛型T的实际类型
25     Type genericSuperclass = clazz.getGenericSuperclass();
26     if (genericSuperclass instanceof Class) {
27       // try to climb up the hierarchy until meet something useful
28       if (TypeReference.class != genericSuperclass) {
29         return getSuperclassTypeParameter(clazz.getSuperclass());
30       }
31       throw new TypeException("'" + getClass() + "' extends TypeReference but misses the type parameter. "
32         + "Remove the extension or add a type parameter to it.");
33     }
34     //获取泛型<T>中的T类型
35     Type rawType = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
36     // TODO remove this when Reflector is fixed to return Types
37     if (rawType instanceof ParameterizedType) {
38       rawType = ((ParameterizedType) rawType).getRawType();
39     }
40     return rawType;
41   }
42 
43   public final Type getRawType() {
44     return rawType;
45   }
46 
47   @Override
48   public String toString() {
49     return rawType.toString();
50   }
51 
52 }

  这个抽象类为是吃BaseTypeHandler所继承的,也尽管意味着几乎有的内置类型处理器都延续了是仿佛,那么是项目引用的目的究竟是什么啊?

  这个题目稍后再说,我们先解析下源码:

  这个看似在那个不论是参构造器中经调用getSuperclassTypeParameter()方法呢那中间定义的final型字段rawType赋值,其参数是getClass()方法的结果,这是Object类中定义的道,这个法子返回的是时下仿佛(实例)的类类型。

  重点在getSuperclassTypeParameter()方法吃:

    第一步:通过加参数的getGenericSuperclass()方法来获得该类类型的直达一级路(直接超类,父类,即参数看似项目继承的类的项目)并蕴含参数类型,即带泛型。如果假定拿走不带泛型的父类可应用getSuperclass()方法。

    第二步:判断第一步获取的类是否是Class类的实例

  Class类的实例有哪些吧?

  其实每一个接近都是Class类的实例,Class类是指向Java中类的泛,它自己吗是一个类,但她是由其他类及平等层次的接近,是近似的顶层抽象。从JDK文档中只是得到知“Instances
of the class represent classes and interfaces in a running Java
application.”(意为:Class的实例表示的是以一个周转的下被的所有类和接口)

 ,那么我们虽知晓了,Class类的实例就是接口和类似。那么Java中产生哪不是Class类的实例呢?泛型类,不错,如果一个类似是泛型类,那么他尽管不再是Class类的实例,为什么吧?

  泛型类是Java中同样栽独特之留存,它一般用于传递类,类似于一般方法被传送对象的概念,它不是粗略的类似,而是相同种植含抽象概念性质的一律栽恍若,它见面透过所传递的接近(参数化类)来指定当前仿佛所表示的底属基本类型受到的啦一样近乎品种。(通过个别种植档次来规定具体的类型)

    第二步:如果第一步获取的类型式带泛型的档次,那么判断不成立,则会一直执行第35行代码,将该项目大转为参数化类型,使用那getActualTypeArguments()方法来收获其参数类型(泛型类型),因为该方式得到之泛型类型或者未是一个,所以回来的凡一个往往组,但是我们这里只会得到一个,所以得到第一只即可。

    但是若第一步获取之型不牵动泛型,那么即便会见进去标准里推行,再次判断,获取的品类是否是TypeReference类型,如果不是欠项目,则实施递归操作,如果是欠种,那么证明第一步通过getGenericSuperclass()获取带泛型的类时也获取到泛型(因为MyBatis中TypeReference是泛型类),则证实程序出错,此处抛来档次大,提示丢失泛型。

    第三步:如果第二步判断不通过,则会履地35实践代码,来收获参数类型,然后对得到的参数类型进行判定如果该档或者参数化类型(仍然蕴藏泛型,即泛型嵌套的模式),那么即便待重实施getActualTypeArguments()方法来收获其泛型类型(参数类型),最后以欠项目返回(赋值给字段)

  为什么偏偏会收获两不善啊?因为,通过事先的接近架构我们已经了解,具体的色处理器最多但见面存在个别叠继承。

  最后说一下,这个类型引用的目的,它便是以具备这个实际的种处理器所拍卖的Java类型的原生类型。我们得以看到在此类中还有少独艺术getRawType()和toString()方法,这半单法子都是public修饰的,是对外公开之道,那么为就算象征这原本生类型是以吃表面调用而设。

  通过搜索发现,getRawType()方法要为调用的地方在TypeHandlerRegistry(类型处理器注册器)中,在从来不点名JavaType而只有TypeHandler的动静下,调用该TypeHandler的getRawType()方法来赢得其原生类型(即参数类型)来当其JavaType来进展项目处理器的挂号。

2.4
基础项目处理器:BaseTypeHandler

  BaseTypeHandler继承了TypeReference抽象类,实现了TypeHandler接口,它自身还是抽象类,在它们其中简单的兑现了TypeHandler接口中定义的季个点子吃的一些功能,所谓部分机能是指仅兑现了有着项目处理器公共部分,具体的不等处理部分则还是顶由具体的路处理器来协调实现,所有它其中又定义了季只抽象类,用来指导具体项目处理器的贯彻。

  BaseTypeHandler中任重而道远对安参数与收获返回结果经常数位null的情形开展了处理,具体的参数设置方法和结果取方式还是由于具体的花色处理器来贯彻的。

1     //非NULL情况,怎么设参数还得交给不同的子类完成
2   public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
3 
4     //以下3个方法是取得可能为null的结果,具体交给子类完成
5   public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
6 
7   public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;
8 
9   public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;

  上面的季单主意就是是BaseTypeHandler中定义之虚幻方法。MyBatis内置的型处理器几乎都是经持续实现者的季单艺术来好最终定义之。

2.5
类型处理器:StringTypeHandler

  我们看个简易的例子来解一下之历程。下面是字符串类型处理器:StringTypeHandler的源码

 1 package org.apache.ibatis.type;
 2 
 3 import java.sql.CallableStatement;
 4 import java.sql.PreparedStatement;
 5 import java.sql.ResultSet;
 6 import java.sql.SQLException;
 7 /**
 8  * String类型处理器
 9  * 调用PreparedStatement.setString, ResultSet.getString, CallableStatement.getString
10  */
11 public class StringTypeHandler extends BaseTypeHandler<String> {
12 
13   @Override
14   public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
15       throws SQLException {
16     ps.setString(i, parameter);
17   }
18 
19   @Override
20   public String getNullableResult(ResultSet rs, String columnName)
21       throws SQLException {
22     return rs.getString(columnName);
23   }
24 
25   @Override
26   public String getNullableResult(ResultSet rs, int columnIndex)
27       throws SQLException {
28     return rs.getString(columnIndex);
29   }
30 
31   @Override
32   public String getNullableResult(CallableStatement cs, int columnIndex)
33       throws SQLException {
34     return cs.getString(columnIndex);
35   }
36 }

  上面的源码完美的注释了事先的辨析,具体的种处理器中单独需要实现即四独方式即可,前提是该后续了BaseTypeHandler抽象类。

  其中设置参数的方式中实际的兑现调用了PreparedStatement的setString()方法,这个是咱格外熟悉的办法。同样的,在获结果的点子吃吗是由此调用ResultSet的getString()方法,和CallableStatement的getString()方法来成功具体的机能。这曾经是MyBatis中最底层的逻辑了,因为她一直调用了JDK
API来促成力量。

2.6 
未知类型处理器:UnknownTypeHandler

   这个是MyBatis中定义的一个较为突出之类型处理器,虽然该中间贯彻和普通的项目处理器而产生同样办法,但是其兼具一些异的地方,所以单独将出去说一样游说。

  通过项目处理器注册器中之注册信息可以见见这种类型处理器所对应的JavaType是Object类型,对应之JdbcType是OTHER类型,这个OTHER是什么类型?我们得这样明白,市面上数据库种类层出不穷,而且各有特点,这些数据库产品就满足SQL规范,同时也生独家的恢弘及强化,每个数据库里还有一些于定义之光当其中间由作用的数据类型,而这些项目反映至Java中之后是Object类型时,这里就是用那联合定义也OTHER类型。

 1   private static final ObjectTypeHandler OBJECT_TYPE_HANDLER = new ObjectTypeHandler();
 2 
 3   private TypeHandlerRegistry typeHandlerRegistry;
 4 
 5   public UnknownTypeHandler(TypeHandlerRegistry typeHandlerRegistry) {
 6     this.typeHandlerRegistry = typeHandlerRegistry;
 7   }
 8 
 9   @Override
10   public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
11       throws SQLException {
12     TypeHandler handler = resolveTypeHandler(parameter, jdbcType);
13     handler.setParameter(ps, i, parameter, jdbcType);
14   }
15 
16   @Override
17   public Object getNullableResult(ResultSet rs, String columnName)
18       throws SQLException {
19     TypeHandler<?> handler = resolveTypeHandler(rs, columnName);
20     return handler.getResult(rs, columnName);
21   }
22 
23   @Override
24   public Object getNullableResult(ResultSet rs, int columnIndex)
25       throws SQLException {
26     TypeHandler<?> handler = resolveTypeHandler(rs.getMetaData(), columnIndex);
27     if (handler == null || handler instanceof UnknownTypeHandler) {
28       handler = OBJECT_TYPE_HANDLER;
29     }
30     return handler.getResult(rs, columnIndex);
31   }
32 
33   @Override
34   public Object getNullableResult(CallableStatement cs, int columnIndex)
35       throws SQLException {
36     return cs.getObject(columnIndex);
37   }

  源码分析:在UnknownTypeHandler中的季独道中,除对存储过程结果取得多少的情状外,其余三单方式的兑现清一色接近,都是预先通过不同的resolveTypeHandler()方法来得到具体的TypeHandler,然后调用具体TypeHandler的照应措施来好功能。那么UnknownTypeHandler中之要紧就是集中在及时三个resolveTypeHandler()方法中了。

 1   private TypeHandler<? extends Object> resolveTypeHandler(Object parameter, JdbcType jdbcType) {
 2     TypeHandler<? extends Object> handler;
 3     if (parameter == null) {
 4       handler = OBJECT_TYPE_HANDLER;
 5     } else {
 6       handler = typeHandlerRegistry.getTypeHandler(parameter.getClass(), jdbcType);
 7       // check if handler is null (issue #270)
 8       if (handler == null || handler instanceof UnknownTypeHandler) {
 9         handler = OBJECT_TYPE_HANDLER;
10       }
11     }
12     return handler;
13   }
14 
15   private TypeHandler<?> resolveTypeHandler(ResultSet rs, String column) {
16     try {
17       Map<String,Integer> columnIndexLookup;
18       columnIndexLookup = new HashMap<String,Integer>();
19       ResultSetMetaData rsmd = rs.getMetaData();
20       int count = rsmd.getColumnCount();
21       for (int i=1; i <= count; i++) {
22         String name = rsmd.getColumnName(i);
23         columnIndexLookup.put(name,i);
24       }
25       Integer columnIndex = columnIndexLookup.get(column);
26       TypeHandler<?> handler = null;
27       if (columnIndex != null) {
28         handler = resolveTypeHandler(rsmd, columnIndex);
29       }
30       if (handler == null || handler instanceof UnknownTypeHandler) {
31         handler = OBJECT_TYPE_HANDLER;
32       }
33       return handler;
34     } catch (SQLException e) {
35       throw new TypeException("Error determining JDBC type for column " + column + ".  Cause: " + e, e);
36     }
37   }
38 
39   private TypeHandler<?> resolveTypeHandler(ResultSetMetaData rsmd, Integer columnIndex) throws SQLException {
40     TypeHandler<?> handler = null;
41     JdbcType jdbcType = safeGetJdbcTypeForColumn(rsmd, columnIndex);
42     Class<?> javaType = safeGetClassForColumn(rsmd, columnIndex);
43     if (javaType != null && jdbcType != null) {
44       handler = typeHandlerRegistry.getTypeHandler(javaType, jdbcType);
45     } else if (javaType != null) {
46       handler = typeHandlerRegistry.getTypeHandler(javaType);
47     } else if (jdbcType != null) {
48       handler = typeHandlerRegistry.getTypeHandler(jdbcType);
49     }
50     return handler;
51   }
52 
53   private JdbcType safeGetJdbcTypeForColumn(ResultSetMetaData rsmd, Integer columnIndex) {
54     try {
55       return JdbcType.forCode(rsmd.getColumnType(columnIndex));
56     } catch (Exception e) {
57       return null;
58     }
59   }
60 
61   private Class<?> safeGetClassForColumn(ResultSetMetaData rsmd, Integer columnIndex) {
62     try {
63       return Resources.classForName(rsmd.getColumnClassName(columnIndex));
64     } catch (Exception e) {
65       return null;
66     }
67   }

  第一独resolveTypeHandler方法是由于安装参数的道调用的,目的在于得到真正的TypeHandler来进展路处理。如果其参数parameter为null,那么直接拿TypeHandler设定也ObjectTypeHandler,如果parameter不为null,则一直打品种处理器注册器中拿走对应JavaType与JdbcType的花色处理器,这里在一个#270BUG,针对无法再次种处理器注册器中赢得TypeHandler获取获取到之是UnknownTypeHandler的景展开再次拍卖:赋值ObjectTypeHandler。

  第二个resolveTypeHandler方法是受通过列名来收获结果数据的措施所调用的,目的同上。首先通过结果集原数据以结果集中之数码循环存放到一个HashMap集合中(以列名为键,列下标为价值),然后从中获得给定列名之下标值,如果凑中有拖欠列名(即会收获到列下标),则调用第三单resolveTypeHandler()方法通过列下标方式来博取具体TypeHandler。当然如果无在这个列名(亦即获得不顶列下标),则直赋值ObjectTypeHandler。

  第三只resolveTypeHandler方法是深受通过列下标来博取结果数据的方法所调用的,同时为叫第二单resolveTypeHandler方法所调用。分别通过safeGetJdbcTypeForColumn()方法及safeGetClassForColumn()方法来抱列下标所对应数据的JdbcType与JavaType,然后针对获得到的JdbcType和JavaType来起品类处理器注册器中赢得具体的品类处理器。这里分三栽状态来赢得:jdbcType与JavaType均不呢null的事态、只有JavaType不为null的状况与只有JdbcType不也null的状态,三者情况分别调用三种植getTypeHandler()方法来好得功能。

  总结:由此可见UnknownTypeHandler是千篇一律种中型处理器,或者给代理项目处理器,因为她自己并无会见真贯彻拍卖功能,它就是通过获取相应之类别处理器来调动用那拍卖功能来就功能。

3、自定义类型处理器

  有关自定义类型处理器,我们唯有做简单介绍,其实她也十分是概括,我们要继续BaseTypeHandler<T>抽象类即可,实现中的季独道。我们这里选出个大概的例证,假如说MyBatis内置的StringTypeHandler无法满足我们的需要,我们可对那进展扩张自定义,我们打定义一个初的字符串类型处理器:MyStringTypeHandler,代码如下:

 1 package org.apache.ibatis.type;
 2 
 3 import java.sql.CallableStatement;
 4 import java.sql.PreparedStatement;
 5 import java.sql.ResultSet;
 6 import java.sql.SQLException;
 7 
 8 public class MyStringTypeHandler extends BaseTypeHandler<String> {
 9 
10     @Override
11     public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
12             throws SQLException {
13         System.out.println("新的逻辑");
14         ps.setString(i, parameter);
15         System.out.println("新的逻辑");
16     }
17 
18     @Override
19     public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
20         System.out.println("新的逻辑");
21         return rs.getString(columnName);
22     }
23 
24     @Override
25     public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
26         System.out.println("新的逻辑");
27         return rs.getString(columnIndex);
28     }
29 
30     @Override
31     public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
32         System.out.println("新的逻辑");
33         return cs.getString(columnIndex);
34     }
35 
36 }

  定义好路处理器之后,然后我们用的饶是将自定义之色处理器注册到TypeHandlerRegistry中,方法吧简要。

1   <typeHandlers>
2         <typeHandler handler="org.apache.ibatis.type.MyStringTypeHandler"/>
3   </typeHandlers>

  当然我们呢可以指定JavaType与jdbcType,获取第一手利用package方式进行设置,但是只要你只是从定义了深少之类别处理器,没有必要运用package方式设置,因为这种方式会扫描整个包下的好像,无形中造成了时延。

  然后斯新的品种处理器就是会补加至TypeHandlerRegistry中了,它见面当暗偷实现力量。

4、总结

  至此我们以Type模块解析了,说之十分是粗糙,但眼看是友善修提高的过程,特此记录,期待下一样首。

 

网站地图xml地图