Mybatis源码分析:类型处理器TypeHandler

类型处理器TypeHandler

  TypeHandler是Mybatis中一个非常重要的接口,用于处理参数类型,包括入参形式和返回结果集相关参数的转换。该接口定义了以下方法。其方法实现已经由子类BaseTypeHandler已经实现了。

  • void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
  • T getResult(ResultSet rs, String columnName) throws SQLException;
  • T getResult(ResultSet rs, int columnIndex) throws SQLException;
  • T getResult(CallableStatement cs, int columnIndex) throws SQLException;

TypeHandler的继承关系和BaseTypeHandler中针对上述方法的实现。

  可以看到mybatis提供了多种数据类型的实现用于针对Java到Sql数据类型的转换。这些数据类型包括byte,short,Integer,BigDecimal,float,double,long,String,enum,Object,bool,blob,clob和包括时间日期相关的处理,这些类型处理器主要是实现了BaseTypeHandler类中所定义的方法,因为BaseTypeHandler属于抽象类,在实现了接口方法的基础上,又添加了相关的扩展方法。首先看看BaseTypeHandler提供的源码实现,在实例化一个BaseTypeHandler的时候,必须提供Configuration配置类,这里顺带说一下。事实上Configuration类存储类你想要的任何配置信息和元数据信息,充当着的mybatis的全局容器,比如我们在写mapper文件或者配置文件时,通常将参数类型或者返回值类型简写,如下代码:

  <select id="list" resultType="map" useCache="true" >
        SELECT id,name,age from STUDENT
    </select>

  该配置将返回类型设置为map,于是mybatis会将查询结果以map形式展现。可以简写的原因是mybatis在实例化Configuration类时,初始化了一个别名注册器TypeAliasRegistry,别名注册器提供了数据类型简称和对应的数据类型。其构造方法代码如下所示,到这里就知道为什么我们只需要提供简称就行了,类型处理器会使用Configuration已经实例化好的别名注册器,寻找匹配的数据类型。除了TypeAliasRegistry别名注册器,Configuration还提供了很多的注册器,比如MapperRegistry,LanguageDriverRegistry,TypeHandlerRegistry等等。关于Configuration和注册器的知识后面再继续讲解。

 1 public TypeAliasRegistry() {
 2     registerAlias("string", String.class);
 3 
 4     registerAlias("byte", Byte.class);
 5     registerAlias("long", Long.class);
 6     registerAlias("short", Short.class);
 7     registerAlias("int", Integer.class);
 8     registerAlias("integer", Integer.class);
 9     registerAlias("double", Double.class);
10     registerAlias("float", Float.class);
11     registerAlias("boolean", Boolean.class);
12 
13     registerAlias("byte[]", Byte[].class);
14     registerAlias("long[]", Long[].class);
15     registerAlias("short[]", Short[].class);
16     registerAlias("int[]", Integer[].class);
17     registerAlias("integer[]", Integer[].class);
18     registerAlias("double[]", Double[].class);
19     registerAlias("float[]", Float[].class);
20     registerAlias("boolean[]", Boolean[].class);
21 
22     registerAlias("_byte", byte.class);
23     registerAlias("_long", long.class);
24     registerAlias("_short", short.class);
25     registerAlias("_int", int.class);
26     registerAlias("_integer", int.class);
27     registerAlias("_double", double.class);
28     registerAlias("_float", float.class);
29     registerAlias("_boolean", boolean.class);
30 
31     registerAlias("_byte[]", byte[].class);
32     registerAlias("_long[]", long[].class);
33     registerAlias("_short[]", short[].class);
34     registerAlias("_int[]", int[].class);
35     registerAlias("_integer[]", int[].class);
36     registerAlias("_double[]", double[].class);
37     registerAlias("_float[]", float[].class);
38     registerAlias("_boolean[]", boolean[].class);
39 
40     registerAlias("date", Date.class);
41     registerAlias("decimal", BigDecimal.class);
42     registerAlias("bigdecimal", BigDecimal.class);
43     registerAlias("biginteger", BigInteger.class);
44     registerAlias("object", Object.class);
45 
46     registerAlias("date[]", Date[].class);
47     registerAlias("decimal[]", BigDecimal[].class);
48     registerAlias("bigdecimal[]", BigDecimal[].class);
49     registerAlias("biginteger[]", BigInteger[].class);
50     registerAlias("object[]", Object[].class);
51 
52     registerAlias("map", Map.class);
53     registerAlias("hashmap", HashMap.class);
54     registerAlias("list", List.class);
55     registerAlias("arraylist", ArrayList.class);
56     registerAlias("collection", Collection.class);
57     registerAlias("iterator", Iterator.class);
58 
59     registerAlias("ResultSet", ResultSet.class);
60   }
 1  public Configuration() {
 2     typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
 3     typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
 4 
 5     typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
 6     typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
 7     typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
 8 
 9     typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
10     typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
11     typeAliasRegistry.registerAlias("LRU", LruCache.class);
12     typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
13     typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
14 
15     typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
16 
17     typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
18     typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
19 
20     typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
21     typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
22     typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
23     typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
24     typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
25     typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
26     typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
27 
28     typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
29     typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
30 
31     languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
32     languageRegistry.register(RawLanguageDriver.class);
33   }

BaseTypeHandler

BaseTypeHandler是TypeHandler的实现类,除了实现了接口本身的方法外,还扩展了相关的方法,后续的各类型处理器均实现了BaseTypeHandler的抽象方法。首先看看BaseTypeHandler对接口方法的实现

setParameter()方法用于为PreparedStatement对象设置对应的参数类型,如果参数和JDBC类型均未设置,那么抛出TypeException,如果参数为空,那么设置参数类型为NULL,如果参数和JDBC都不为NULL,则设置指定的参数类型。在设置参数时,存在setNonNullParameter(ps, i, parameter, jdbcType);方法,该方法在BaseTypeHandler是抽象方法,假设该参数需要设置数组型参数,首先找到ArrayTypeHandler子类,观察方法中的实现.如下代码:

  Array array = rs.getArray(columnName);
    return array == null ? null : array.getArray();

该实现方法中实例化一个数组对象,返回一个数组,可以推论出其他的类型处理器在设置参数时,也是设置对应的参数类型的

 1  public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
 2       //如果参数和JDBC类型均未设置,那么抛出TypeException,如果参数为空,那么设置参数类型为NULL
 3     if (parameter == null) {
 4       if (jdbcType == null) {
 5         throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
 6       }
 7       try {
 8         ps.setNull(i, jdbcType.TYPE_CODE);
 9       } catch (SQLException e) {
10         throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
11                 "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
12                 "Cause: " + e, e);
13       }
14     } else {
15       try {
16           //设置指定的参数类型
17         setNonNullParameter(ps, i, parameter, jdbcType);
18       } catch (Exception e) {
19         throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
20                 "Try setting a different JdbcType for this parameter or a different configuration property. " +
21                 "Cause: " + e, e);
22       }
23     }
24   }

getResult()方法用于为设置字段对应的结果集,可以通过字段的名字和下标来设置对应的结果集,如下列代码所示,该方法也是BaseTypeHandler类的一个抽象方法,通过子类的getNullableResult()方法实现结果集的填充,仍以ArrayTypeHandler为例,查看相关的实现方法,可以看到ArrayTypeHandler只是做了对ResultSet简单的封装,想要获取什么样的数据类型就调用ResultSet中的方法。

 1 public T getResult(ResultSet rs, String columnName) throws SQLException {
 2     T result;
 3     try {
 4       result = getNullableResult(rs, columnName);
 5     } catch (Exception e) {
 6       throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);
 7     }
 8     if (rs.wasNull()) {
 9       return null;
10     } else {
11       return result;
12     }
13   }
 1 @Override
 2   public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
 3     Array array = rs.getArray(columnName);
 4     return array == null ? null : array.getArray();
 5   }
 6 
 7   @Override
 8   public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
 9     Array array = rs.getArray(columnIndex);
10     return array == null ? null : array.getArray();
11   }

JdbcType:

在介绍设置参数的时候,涉及到JdbcType类,这个类本身是枚举类,所做的事情很简单,将java.sql.Types类包装成mybatis可用的JDBC类型,代码如下所示:

 1 /**
 2  *    Copyright 2009-2015 the original author or authors.
 3  *
 4  *    Licensed under the Apache License, Version 2.0 (the "License");
 5  *    you may not use this file except in compliance with the License.
 6  *    You may obtain a copy of the License at
 7  *
 8  *       http://www.apache.org/licenses/LICENSE-2.0
 9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 package org.apache.ibatis.type;
17 
18 import java.sql.Types;
19 import java.util.HashMap;
20 import java.util.Map;
21 
22 /**
23  * @author Clinton Begin
24  */
25 public enum JdbcType {
26   /*
27    * This is added to enable basic support for the
28    * ARRAY data type - but a custom type handler is still required
29    */
30   ARRAY(Types.ARRAY),
31   BIT(Types.BIT),
32   TINYINT(Types.TINYINT),
33   SMALLINT(Types.SMALLINT),
34   INTEGER(Types.INTEGER),
35   BIGINT(Types.BIGINT),
36   FLOAT(Types.FLOAT),
37   REAL(Types.REAL),
38   DOUBLE(Types.DOUBLE),
39   NUMERIC(Types.NUMERIC),
40   DECIMAL(Types.DECIMAL),
41   CHAR(Types.CHAR),
42   VARCHAR(Types.VARCHAR),
43   LONGVARCHAR(Types.LONGVARCHAR),
44   DATE(Types.DATE),
45   TIME(Types.TIME),
46   TIMESTAMP(Types.TIMESTAMP),
47   BINARY(Types.BINARY),
48   VARBINARY(Types.VARBINARY),
49   LONGVARBINARY(Types.LONGVARBINARY),
50   NULL(Types.NULL),
51   OTHER(Types.OTHER),
52   BLOB(Types.BLOB),
53   CLOB(Types.CLOB),
54   BOOLEAN(Types.BOOLEAN),
55   CURSOR(-10), // Oracle
56   UNDEFINED(Integer.MIN_VALUE + 1000),
57   NVARCHAR(Types.NVARCHAR), // JDK6
58   NCHAR(Types.NCHAR), // JDK6
59   NCLOB(Types.NCLOB), // JDK6
60   STRUCT(Types.STRUCT),
61   JAVA_OBJECT(Types.JAVA_OBJECT),
62   DISTINCT(Types.DISTINCT),
63   REF(Types.REF),
64   DATALINK(Types.DATALINK),
65   ROWID(Types.ROWID), // JDK6
66   LONGNVARCHAR(Types.LONGNVARCHAR), // JDK6
67   SQLXML(Types.SQLXML), // JDK6
68   DATETIMEOFFSET(-155); // SQL Server 2008
69 
70   public final int TYPE_CODE;
71   private static Map<Integer,JdbcType> codeLookup = new HashMap<Integer,JdbcType>();
72 
73   static {
74     for (JdbcType type : JdbcType.values()) {
75       codeLookup.put(type.TYPE_CODE, type);
76     }
77   }
78 
79   JdbcType(int code) {
80     this.TYPE_CODE = code;
81   }
82 
83   public static JdbcType forCode(int code)  {
84     return codeLookup.get(code);
85   }
86 
87 }

猜你喜欢

转载自www.cnblogs.com/zhengzuozhanglina/p/11249657.html