Mybatis 源码学习-类型转换(TypeHandler)

Posted 凉茶方便面

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis 源码学习-类型转换(TypeHandler)相关的知识,希望对你有一定的参考价值。

历史文章:
Mybatis 源码学习(7)-反射工具(ObjectWrapper & MetaObject)


JDBC 规范中定义的数据类型和 Java语言中的数据类型并非完全对应,因此需要在操作 Statement 时需要将 Java 类型转为 JDBC 类型,而处理 Result时,需要将JDBC 类型,转为 Java 类型。Mybatis 使用 TypeHandler 处理这种类型转换逻辑,另外 Mybatis 使用 JdbcType 枚举了所有的 JDBC类型,并将 JDBC 规范中的原始数值作为 TYPE_CODE,构造出 codeLookup 作为 code 和枚举的映射 Map。

Mybatis 中存在多个 TypeHandler 的子类,并且提供 BaseTypeHandler 作为默认实现,由具体子类继承实现具体的逻辑,但是大部分子类都会直接使用 Statement 的对应方法。

TypeHandler 提供了四个方法,其中:setParameter 负责将 JdbcType 转为 Java 类型;三个重载的 getResult 负责将数据由 Java 类型转为 JdbcType。

public interface TypeHandler<T> 

  // 通过 PreparedStatement 为 SQL 语句绑定参数,将 Java 类型转为 JDBC 数据
  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  // 通过 Statement 获取指定列的值,将数据从 JDBC 数据转为 Java 类型
  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 外,Mybatis 还提供了 BaseTypeHandler 作为基类,它实现 TypeHandler 接口并继承 TypeReference 抽象类。

BaseTypeHandler 实现了基本的 setParameter 和 getResult 方法,它们都可以以默认方式处理 null 的情况,但是非 null 的情况交给了具体实现类处理。

public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException 
  // 参数是 null
  if (parameter == null) 
    if (jdbcType == null) 
      throw new TypeException(“…”);
    
    try 
      // 直接按照 Jdbc 类型的 null 存储
      ps.setNull(i, jdbcType.TYPE_CODE);
     catch (SQLException e) 
      throw new TypeException(“…”);
    
   else 
    try 
      // 处理非 null,由具体子类实现
      setNonNullParameter(ps, i, parameter, jdbcType);
     catch (Exception e) 
      throw new TypeException(“…”);
    
  

public T getResult(ResultSet rs, String columnName) throws SQLException 
  T result;
  try 
    // 获取允许为 null 的结果集,由具体子类实现
    result = getNullableResult(rs, columnName);
   catch (Exception e) 
    throw new ResultMapException(“…");
  
  if (rs.wasNull()) 
    return null;
   else 
    return result;
  

由于 BaseTypeHandler 的实现类较多,因此以 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); // 直接使用 Statement 的方法
  

  @Override
  public Integer getNullableResult(ResultSet rs, String columnName)
      throws SQLException 
    return rs.getInt(columnName); // 直接使用 ResultSet 的方法,获取指定列的值
  

  @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 可以实现 Java 类型到 JDBC 类型之间的转换,它提供了默认的基类,但是具体的非空数据的转换交由子类实现。另外,TypeHandler 仅用于处理单列数据的转换,对于复杂数据类型的转化,可以使用 <resultMap> 进行映射。


参考文档:《Mybatis 技术内幕》

本文的基本脉络参考自《Mybatis 技术内幕》,编写文章的原因是希望能够系统地学习 Mybatis 的源码,但是如果仅阅读源码或者仅从官方文档很难去系统地学习,因此希望参考现成的文档,按照文章的脉络逐步学习。


欢迎关注我的公众号:我的搬砖日记,我会定时分享自己的学习历程。

以上是关于Mybatis 源码学习-类型转换(TypeHandler)的主要内容,如果未能解决你的问题,请参考以下文章

Mybatis 源码学习(10)-类型转换(TypeAliasRegistry)

Mybatis 源码学习(10)-类型转换(TypeAliasRegistry)

Mybatis 源码学习-类型转换(TypeHandler)

Mybatis 源码学习-类型转换(TypeHandler)

MyBatis源码阅读: MyBatis基础模块-类型转换模块

Mybatis 源码学习(11)-日志模块