MyBatis源码剖析

[MyBatis源码分析 - 类型模块 - 组件一] TypeH

2020-11-01  本文已影响0人  小胡_鸭

一、简介

  不管是参数绑定,还是结果映射,mybatis 都要完成 Java 类型和 Jdbc 类型数据的相互转换,在框架中为了统一类型转换处理器的行为,定义了 TypeHandler 接口,并实现了多种不同的类型处理器。

二、TypeHandler 接口

  TypeHandler 为所有类型转换器都要实现的接口,定义了 #setParameter() 方法来为SQL绑定参数,定义了重载的 #getResult() 方法来将结果映射为Java对象,接口定义如下:

public interface TypeHandler<T> {

  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;

}

【解析】
  接口定义中的泛型 T 就表示 Java 类型,各个特定的转换器实现类根据转换的 Java 类型指定其具体类型。

  #setParameter() 方法完成 javaType 到 jdbcType 的转换。PreparedStatement ps 表示一个预编译的SQL语句对象,int i 指定了绑定该语句中的参数的位置,T parameter 为要被转换的 Java 对象,JdbcType jdbcType 为目的转化类型,当传入的 Java 对象是空时,参数绑定需要调用 PreparedStatement .setNull() 方法,该方法的第二个参数即为 Jdbc 类型。

  重载的三个 #getResult() 方法分别根据数据列名、列索引来将结果集中的数据转化为 Java 类型,完成 jdbcType 到 javaType 的转换。

    public Role getRole(Long id) {
        Connection connection = getConnection();
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            ps = connection.prepareStatement("select id, role_name, note from t_role where id = ?");
            ps.setLong(1, id);
            rs = ps.executeQuery();
            while (rs.next()) {
                Long roleId = rs.getLong("id");
                String roleName = rs.getString("role_name");
                String note = rs.getString("note");
                Role role = new Role();
                role.setId(id);
                role.setRoleName(roleName);
                role.setNote(note);
                return role;
            }
        } catch (Exception e) {
            Logger.getLogger(JdbcExample.class.getName()).log(Level.SEVERE, null, e);
        } finally {
            this.close(rs, ps, connection);
        }
        return null;
    }

  再次将 反射器模块 开头的这段JDBC代码拿出来回顾一下,前面的 ps.setLong(1, id) 就是将 javaType 转换为 jdbcType,而后面的 rs.getString("role_name") 就是将jdbcType 转换为 javaType,可以看出不管是 PreparedStatement 还是 ResultSet 的方法名都带有类型名,所以下面介绍的方法转换器的实现中要根据处理的不同目标类型,调用不同的 getType/setType 方法。

【注意】如果将一张数据表中的一行数据映射成一个Java对象,则Java对象中的每个成员属性跟表中的一行的每个列映射,上述的类型转换器,实际上是完成一个属性跟一个数据库字段列的映射,因为Java对象有多个成员,数据表有多个字段,所以实际映射一行数据的时候,要有多个类型转换器参与处理。上述的Java对象,实际是指一个成员属性对应的对象。

三、BaseTypeHandler

  BaseTypeHandler 是一个承上启下的抽象类,它实现了 TypeHandler 接口,又被所有具体的转换器类所继承,因为SQL绑定参数时可能传入一个空的参数,结果映射时也可能获取到一个空的数据,这里需要一点特别的处理,这些都放在 BaseTypeHandler 中统一实现,简化定义类型转换器的工作。

  BaseTypeHandler 封装了 Configuration 对象作为成员,该对象与 mybatis 全局配置中的配置信息映射,因为在 mybatis-config.xml 用户可以自定义类型转换器处理一些特殊的类型,比如枚举。

public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {

  protected Configuration configuration;

  public void setConfiguration(Configuration c) {
    this.configuration = c;
  }

  // other code
}

  BaseTypeHandler 还继承了一个带泛型的父类 TypeReference,该类主要用来解析泛型的具体类型,后面会介绍。

1、void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)

【功能】使用 Java 类型对象为 SQL 绑定参数。
【源码与注解】

  @Override
  public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
    // (1)当Java对象为空时,调用 PreparedStatement.setNull() 绑定参数
    if (parameter == null) {
      // 上述方法需要确定JdbcType,所以如果jdbcType也为空,该方法无法执行,抛出异常
      if (jdbcType == null) {
        throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
      }
      try {
        ps.setNull(i, jdbcType.TYPE_CODE);
      } catch (SQLException e) {
        // 传入的JdbcType类型不合适,抛出异常
        throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
                "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
                "Cause: " + e, e);
      }
    } else {
      try {
        // (2)调用类中定义的抽象方法,该方法在继承了BaseTypeHandler的子类中有具体且不同的实现
        setNonNullParameter(ps, i, parameter, jdbcType);
      } catch (Exception e) {
        throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
                "Try setting a different JdbcType for this parameter or a different configuration property. " +
                "Cause: " + e, e);
      }
    }
  }

  public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

【解析】

2、T getResult(ResultSet rs, String columnName)

【功能】根据列名对 SQL 执行结果 ResultSet 对象进行映射成 Java 对象。
【源码与注解】

  @Override
  public T getResult(ResultSet rs, String columnName) throws SQLException {
    T result;
    try {
      // 调用子类中实现的抽象方法获取结果值
      result = getNullableResult(rs, columnName);
    } catch (Exception e) {
      throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);
    }
    // 判断映射的结果集是否为空
    if (rs.wasNull()) {
      return null;
    } else {
      return result;
    }
  }

  public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;

【解析】
  这里代码第一眼看有点费解,为什么先尝试从结果集中获取结果,再来判断结果集是否空呢?从 ResetSet.wasNull() 的注释中可以看出,需要调用 get 方法,才能使用该方法,所以这里要先尝试先调用 #getNullableResult() 获取结果,该方法的具体实现由各具体的转换器子类实现。


  其他两个重名的重载方法跟上述方法类似,不再赘述。

四、各种类型转换器

  TypeHandler 有很多类型转化器,下面分类说明。

1、基本数据类型对应的包装类型转换处理器

  Java 中八种基本数据类型分别是 short、int、long、float、double、boolean、byte、char,对应的包装类型分别为 Short、Integer、Long、Float、Double、Boolean、Byte、Character,对应的类型转换器别分为 ShortTypeHandler、IntegerTypeHandler、LongTypeHandler、FloatTypeHandler、DoubleTypeHandler、BooleanTypeHandler、ByteTypeHandler、CharacterTypeHandler,这几个转换器的实现都是类似的,只是调用 PreparedStatement/ResultSet/CallableStatement 对应的 set/get 方法有差异,以 IntegerTypeHandler 为例,源码如下:

public class IntegerTypeHandler extends BaseTypeHandler<Integer> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
      throws SQLException {
    ps.setInt(i, parameter);
  }

  @Override
  public Integer getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    return rs.getInt(columnName);
  }

  @Override
  public Integer getNullableResult(ResultSet rs, int columnIndex)
      throws SQLException {
    return rs.getInt(columnIndex);
  }

  @Override
  public Integer getNullableResult(CallableStatement cs, int columnIndex)
      throws SQLException {
    return cs.getInt(columnIndex);
  }
}

  其他的 TypeHandler 比如 ShortTypeHandler 就是将 #getInt 改成 #getShort,将 #setInt 改成 #setShort,其他类型转换器以此类推。

2、字节、字符串及相关数组对应的类型转换处理器

  在介绍这些转换处理器之前,有必要先了解关于字符、字符串相关的通用 SQL 类型,列表如下:

类型 用途
char(n)/character(n) 字符/字符串。固定长度n
varchar(n)/character varying(n) 字符/字符串。可变长度,最大长度n
blob 二进制形式的长文本数据
clob/text 以字节为单位保存文本大对象
nclob 以 unicode 字符为单位保存文本大对象

   不同数据库数据类型的定义略有不同,不过大同小异,一般都有定长和变长字符类型可选,两者的区别在于,定长类型的数据存储前数据库就分配一块固定的存储空间了,而变长是实际用到多少分配多少。

  mysql 和 oracle 都有 blob 的数据类型,用来存储二进制形式的长文本数据,最大存储空间不同数据库设计略有差异,为了满足不同大小的数据库存储的需求,mysql 还有 tinyblobmediumbloblongblob 的数据类型,oracle 还有 bfile 满足超大数据块的存储需求。

  对于存储文本形式的大数据块,mysql 对应的数据类型为 tinytexttextmediumtextlongtext,而 oracle 则设计了 clobnclobclob 使用数据库编码的字符的定长字节存储文本,nclob 使用 unicode 编码的字符的定长字节存储文本。

(1)与 blob 相关的类型转换处理器

BlobTypeHandler / BlobByteObjectArrayTypeHandler / BlobInputStreamTypeHandler

public class BlobTypeHandler extends BaseTypeHandler<byte[]> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, byte[] parameter, JdbcType jdbcType)
      throws SQLException {
    // 将字节数组转化为流对象
    ByteArrayInputStream bis = new ByteArrayInputStream(parameter);
    ps.setBinaryStream(i, bis, parameter.length);
  }

  @Override
  public byte[] getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    Blob blob = rs.getBlob(columnName);
    byte[] returnValue = null;
    if (null != blob) {
      returnValue = blob.getBytes(1, (int) blob.length());
    }
    return returnValue;
  }
  // 另外两个方法类似

【解析】
  参数绑定时,先将字节数组转化为流对象,再绑定参数,这样数据库在接收数据时是从流对象中读取,直到到达文件末尾,性能更佳;结果映射都是从结果集中获取到一个 Blob 对象,再从该对象中读取字节数组。

  BlobByteObjectArrayTypeHandler 跟 BlobTypeHandler 的处理基本一样,只不过处理的java对象类型为 Byte[],所以处理之前将 Byte[] 转换为 byte[],结果映射时反过来将 byte[] 转换为 Byte[]。

  BlobInputStreamTypeHandler 处理的java数据类型是 InputStream,不过用的不是 #setBinaryStream(),而是 #setBlob(),这里有点疑惑,看 Javadoc 的解释是 #setBlob() 方法会通知驱动发送 Blob 形式的参数值给服务端;而 #setBinaryStream() 方法驱动处理时会有额外的工作决定发送 LONGVARBINARY 还是 Blob 形式的参数值给服务端。至于 LONGVARBINARY 和 Blob 有什么区别?这些类型在 Java 端可能就都是字节数组,但是在数据库驱动中应该进行了封装处理转化为不同的数据类型,但是我们无法得知是怎么处理的,因为驱动一般只对外提供接口。

(2)与 clob 相关的类型转换处理器

ClobTypeHandler / NClobTypeHandler / ClobReaderTypeHandler

public class ClobTypeHandler extends BaseTypeHandler<String> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
      throws SQLException {
    StringReader reader = new StringReader(parameter);
    ps.setCharacterStream(i, reader, parameter.length());
  }

  @Override
  public String getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    String value = "";
    Clob clob = rs.getClob(columnName);
    if (clob != null) {
      int size = (int) clob.length();
      value = clob.getSubString(1, size);
    }
    return value;
  }
  // 另外两个方法类似

【解析】
  参数绑定时,先将字符串转化为 Reader 对象,再绑定参数,该对象为给定的字符数。 当将非常大的 UNICODE 值输入到 LONGVARCHAR 参数时,通过 java.io.Reader 对象发送它可能更实际。 数据将根据需要从流中读取,直到到达文件末尾。 JDBC 驱动程序将执行从 UNICODE 到数据库char格式的所有必要转换。

  NClobTypeHandler 的实现跟 ClobTypeHandler 完全一样,不知道为什么要定义这样一个多余的类型转换处理器?

  ClobReaderTypeHandler 处理的java数据类型是 Reader,不过用的不是 #setCharacterStream(),而是 #setClob(),原理跟上面一样。

(3)与字节相关的类型转换处理器

ByteTypeHandler / ByteArrayTypeHandler / ByteObjectArrayTypeHandler

  代码跟上面类似,不赘述,这里会用到一个用具类,用于 byte[]Byte[] 之间的互相转换,源码如下:

class ByteArrayUtils {

  private ByteArrayUtils() {
    // Prevent Instantiation
  }

  static byte[] convertToPrimitiveArray(Byte[] objects) {
    final byte[] bytes = new byte[objects.length];
    for (int i = 0; i < objects.length; i++) {
      bytes[i] = objects[i];
    }
    return bytes;
  }

  static Byte[] convertToObjectArray(byte[] bytes) {
    final Byte[] objects = new Byte[bytes.length];
    for (int i = 0; i < bytes.length; i++) {
      objects[i] = bytes[i];
    }
    return objects;
  }
}

(4)与字符串相关的类型转换处理器

StringTypeHandler / NStringTypeHandler

  两个类的实现完全一样,java类型是 String,调用 PreparedStatement 的 setString、getString 方法处理。

3、日期时间相关的类型转换处理器

  先了解一下日期时间相关通用 SQL 数据类型,列表如下:

类型 格式 用途
DATE YYYY-MM-DD 日期
TIME HH:MM:SS 时间值
YEAR YYYY 年份值
DATETIME YYYY-MM-DD HH:MM:SS 混合日期和时间值
TIMESTAMP YYYY-MM-DD HH:MM:SS 混合日期和时间值,时间戳

(1)日期相关类型转换处理器

DateTypeHandler / DateOnlyTypeHandler / SqlDateTypeHandler

public class DateTypeHandler extends BaseTypeHandler<Date> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType)
      throws SQLException {
    // 将 java.util.Date 转化为 java.sql.Timestamp,再设置到 ps 中
    ps.setTimestamp(i, new Timestamp((parameter).getTime()));
  }

  @Override
  public Date getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    // 从结果集中获得 Timestamp 的值
    Timestamp sqlTimestamp = rs.getTimestamp(columnName);
    // 将 Timestamp 转化为 Date 类型
    if (sqlTimestamp != null) {
      return new Date(sqlTimestamp.getTime());
    }
    return null;
  }

  // 另外两个方法类似

【解析】
  DateTypeHandlerjava.util.Date 的 JavaType 转化为 timestamp 的 JdbcType,绑定参数时先转化为 java.sql.Timestamp,再设置到 ps 中,结果映射反过来。
  DateOnlyTypeHandlerjava.util.Datejava.sql.Date 之间的转换,处理类似。
  SqlDateTypeHandler 更简单了,传入返回的参数类型就是 java.sql.Date 无需做额外的类型转换。

(2)时间相关类型转换处理器

TimeOnlyTypeHandler / SqlTimeTypeHandler / SqlTimestampTypeHandler

4、枚举相关的类型转换处理器

  MyBatis 内置了两种枚举类型转换处理器 EnumTypeHandlerEnumOrdinalTypeHandler

EnumTypeHandler

【功能】将枚举转化为对应的枚举类型字符串,再绑定参数;结果映射时拿到代表枚举的字符串,再转化为枚举类型。
【源码与注解】

public class EnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {

  private Class<E> type;

  public EnumTypeHandler(Class<E> type) {
    if (type == null) {
      throw new IllegalArgumentException("Type argument cannot be null");
    }
    this.type = type;
  }

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
    // 将 Enum 转换成 String 类型
    if (jdbcType == null) {
      ps.setString(i, parameter.name());
    } else {
      ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE); // see r3589
    }
  }

  @Override
  public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
    // 获得 String 的值
    String s = rs.getString(columnName);
    // 将 String 转换成 Enum 类型
    return s == null ? null : Enum.valueOf(type, s);
  }

  // 另外两个重载方法处理类似
}

【解析】
  假设有一个这样的枚举类,定义如下:

public enum SexEnum {
    FEMALE(1, "女"),
    MALE(0, "男");

    private int code;
    private String name;
    
    
    
    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

  上面 parameter.name() 得到的就是一个 "FEMALE""MALE" 的字符串,存入数据表列中也是存储这么一个值,结果映射时也是用这么一个字符串调用 Enum.valueOf() 转化为对应的枚举类型返回。

EnumOrdinalTypeHandler

【功能】根据枚举类型获取在枚举数组中的索引位置,再将其作为一个整数存储在数据库中。
【源码与注解】

public class EnumOrdinalTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {

  private Class<E> type;
  private final E[] enums;

  public EnumOrdinalTypeHandler(Class<E> type) {
    if (type == null) {
      throw new IllegalArgumentException("Type argument cannot be null");
    }
    this.type = type;
    this.enums = type.getEnumConstants();
    if (this.enums == null) {
      throw new IllegalArgumentException(type.getSimpleName() + " does not represent an enum type.");
    }
  }

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
    // 将 Enum 转换成 int 类型
    ps.setInt(i, parameter.ordinal());
  }

  @Override
  public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
    // 获得 int 的值
    int i = rs.getInt(columnName);
    if (rs.wasNull()) {
      return null;
    } else {
      try {
        // 将 int 转换成 Enum 类型
        return enums[i];
      } catch (Exception ex) {
        throw new IllegalArgumentException("Cannot convert " + i + " to " + type.getSimpleName() + " by ordinal value.", ex);
      }
    }
  }

【解析】
  还是上面的枚举类为例子,SexEnum.FEMALE.ordinal() 得到的是 0,保存到数据库中的就是一个整数,结果映射反过来。

5、对象类型转换处理器

ObjectTypeHandler

【功能】数据库驱动自动根据传入的Java对象类型和数据表字段类型,自动转换。
【源码】

public class ObjectTypeHandler extends BaseTypeHandler<Object> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
      throws SQLException {
    ps.setObject(i, parameter);
  }

  @Override
  public Object getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    return rs.getObject(columnName);
  }

  @Override
  public Object getNullableResult(ResultSet rs, int columnIndex)
      throws SQLException {
    return rs.getObject(columnIndex);
  }

  @Override
  public Object getNullableResult(CallableStatement cs, int columnIndex)
      throws SQLException {
    return cs.getObject(columnIndex);
  }
}

【解析】
  如果数据类型不明确,可以调用 setObject、getObject 方法处理,如果知道数据类型,优先使用指明处理类型的方法。

6、UnknownTypeHandler

  如果转换时,参数绑定传入的参数类型或者结果集数据类型不清晰,则调用 UnknownTypeHandler 先处理解析出具体的类型,再调用对应的 TypeHandler 处理。
【源码分析】

public class UnknownTypeHandler extends BaseTypeHandler<Object> {

  // 静态变量,单例共享
  private static final ObjectTypeHandler OBJECT_TYPE_HANDLER = new ObjectTypeHandler();
  // TypeHandler 注册表
  private TypeHandlerRegistry typeHandlerRegistry;

  public UnknownTypeHandler(TypeHandlerRegistry typeHandlerRegistry) {
    this.typeHandlerRegistry = typeHandlerRegistry;
  }

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
      throws SQLException {
    // 根据传入的参数和jdbcType获得对应 TypeHandler
    TypeHandler handler = resolveTypeHandler(parameter, jdbcType);
    // 使用处理器绑定参数
    handler.setParameter(ps, i, parameter, jdbcType);
  }

  @Override
  public Object getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    // 根据结果集和列名获得对应的处理器
    TypeHandler<?> handler = resolveTypeHandler(rs, columnName);
    // 使用处理器处理结果映射
    return handler.getResult(rs, columnName);
  }

  @Override
  public Object getNullableResult(ResultSet rs, int columnIndex)
      throws SQLException {
    // 根据结果集和列名获得对应的处理器
    TypeHandler<?> handler = resolveTypeHandler(rs.getMetaData(), columnIndex);
    // 从注册表中可能找不到合适的处理器
    if (handler == null || handler instanceof UnknownTypeHandler) {
      handler = OBJECT_TYPE_HANDLER;
    }
    // 使用处理器处理结果映射
    return handler.getResult(rs, columnIndex);
  }

  @Override
  public Object getNullableResult(CallableStatement cs, int columnIndex)
      throws SQLException {
    return cs.getObject(columnIndex);
  }

  private TypeHandler<? extends Object> resolveTypeHandler(Object parameter, JdbcType jdbcType) {
    TypeHandler<? extends Object> handler;
    // 若参数值为空,则使用默认的 ObjectTypeHandler
    if (parameter == null) {
      handler = OBJECT_TYPE_HANDLER;
    // 若参数非空,根据参数类型从注册表中获取处理器
    } else {
      handler = typeHandlerRegistry.getTypeHandler(parameter.getClass(), jdbcType);
      // check if handler is null (issue #270)
      // 若没有获得明确类型的处理器,则使用默认的 ObjectTypeHandler
      if (handler == null || handler instanceof UnknownTypeHandler) {
        handler = OBJECT_TYPE_HANDLER;
      }
    }
    return handler;
  }

  private TypeHandler<?> resolveTypeHandler(ResultSet rs, String column) {
    try {
      // 获得结果集中的元数据信息,并将所有的数据列名和位置索引的映射关系保存在 columnIndexLookup 中
      Map<String,Integer> columnIndexLookup;
      columnIndexLookup = new HashMap<String,Integer>();
      ResultSetMetaData rsmd = rs.getMetaData();
      int count = rsmd.getColumnCount();
      for (int i=1; i <= count; i++) {
        String name = rsmd.getColumnName(i);
        columnIndexLookup.put(name,i);
      }
      // 从 columnIndexLookup 中找到列名对应的位置索引
      Integer columnIndex = columnIndexLookup.get(column);
      TypeHandler<?> handler = null;
      if (columnIndex != null) {
        handler = resolveTypeHandler(rsmd, columnIndex);
      }
      // 若没有找到列名对应的索引,则使用默认的 ObjectTypeHandler
      if (handler == null || handler instanceof UnknownTypeHandler) {
        handler = OBJECT_TYPE_HANDLER;
      }
      return handler;
    } catch (SQLException e) {
      throw new TypeException("Error determining JDBC type for column " + column + ".  Cause: " + e, e);
    }
  }

  private TypeHandler<?> resolveTypeHandler(ResultSetMetaData rsmd, Integer columnIndex) throws SQLException {
    TypeHandler<?> handler = null;
    JdbcType jdbcType = safeGetJdbcTypeForColumn(rsmd, columnIndex);
    Class<?> javaType = safeGetClassForColumn(rsmd, columnIndex);
    // 根据 JdbcType 和 JavaType 从注册表中调用对应的方法获得处理器
    if (javaType != null && jdbcType != null) {
      handler = typeHandlerRegistry.getTypeHandler(javaType, jdbcType);
    } else if (javaType != null) {
      handler = typeHandlerRegistry.getTypeHandler(javaType);
    } else if (jdbcType != null) {
      handler = typeHandlerRegistry.getTypeHandler(jdbcType);
    }
    return handler;
  }

  private JdbcType safeGetJdbcTypeForColumn(ResultSetMetaData rsmd, Integer columnIndex) {
    try {
      // 从 ResultSetMetaData 中,获得字段类型的 JdbcType
      return JdbcType.forCode(rsmd.getColumnType(columnIndex));
    } catch (Exception e) {
      return null;
    }
  }

  private Class<?> safeGetClassForColumn(ResultSetMetaData rsmd, Integer columnIndex) {
    try {
      // 从 ResultSetMetaData 中,获得字段类型的 JavaType
      return Resources.classForName(rsmd.getColumnClassName(columnIndex));
    } catch (Exception e) {
      return null;
    }
  }
}
上一篇 下一篇

猜你喜欢

热点阅读