zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    记录一次电话面试
    记录一次git合并
    HTML通用属性与常见标签
    位运算的应用
    HTML总结
    前端MVC
    常用软件
    docker常用命令
    composer install(update)时出现killed
    优化小技巧:该怎么识别百度蜘蛛呢
  • 原文地址:https://www.cnblogs.com/zhengzuozhanglina/p/11249657.html
Copyright © 2011-2022 走看看