mybatis源码解析之基础模块-TypeHandler

本文主要解析MyBatis TypeHandler模块的源码,包括JdbcType、MappedTypes、MappedJdbcTypes、TypeReference等,分析不同类型转换器的实现,如IntegerTypeHandler、UnknownTypeHandler,并探讨TypeHandlerRegister和ResultSetWrapper的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


在这里插入图片描述

MyBatis源码解析之基础模块—TypeHandler

前文回顾

上一章节我们一起分析了Mybatis的Plugin模块的源码。掌握了如何配置拦截器注解,如何自定义拦截器以及拦截器的执行过程。

在使用Mybatis的过程中,基本上我们都要在xml中编写相应的sql语句以及对应的java属性与字段的转换。那么对于数据库与java之间的转换,Mybatis是怎么做的呢?

接下来本章节我们对MyBatis Type模块类型转换的源码进行分析。

架构设计

按惯例,咱们先了解下Type模块的总体架构设计。

Type模块所在包路径为org.apache.ibatis.type,其对应的类架构设计图如下:

在这里插入图片描述

​ 以上为Type模块的架构逻辑,当然针对不同的类型转换实现,架构图中只展示了IntegerTypeHandler、UnknownTypeHandler两个典型实现。

​ 基于架构图,接下来逐个分析其实现源码。

源码解读

JdbcType

JdbcType就是一个枚举类。该类定义了常用的一些数据类型,比如Integer,Double,Date,Date等,基本上满足了我们开发中常用的数据类型。

package org.apache.ibatis.type;

import java.sql.Types;
import java.util.HashMap;
import java.util.Map;

public enum JdbcType {
   
  INTEGER(Types.INTEGER),
  BIGINT(Types.BIGINT),
  FLOAT(Types.FLOAT),
  REAL(Types.REAL),
  DOUBLE(Types.DOUBLE),
  NUMERIC(Types.NUMERIC),
  DECIMAL(Types.DECIMAL),
  CHAR(Types.CHAR),
  VARCHAR(Types.VARCHAR),
  DATE(Types.DATE),
  BOOLEAN(Types.BOOLEAN)
    ……
    ; // JDBC 4.2 JDK8

  public final int TYPE_CODE;
  private static Map<Integer,JdbcType> codeLookup = new HashMap<>();

  static {
   
    for (JdbcType type : JdbcType.values()) {
   
      codeLookup.put(type.TYPE_CODE, type);
    }
  }

  JdbcType(int code) {
   
    this.TYPE_CODE = code;
  }

  public static JdbcType forCode(int code)  {
   
    return codeLookup.get(code);
  }
}
MappedTypes

该注解接口作用于类型转换的实现类,用于标注要映射的java类型。

public @interface MappedTypes {
   
  /**
   * 返回要映射处理的java类型集合
   */
  Class<?>[] value();
}
MappedJdbcTypes

该注解接口作用于类型转换的实现类,用于标注要映射的数据库类型。

public @interface MappedJdbcTypes {
   
  /**
   * 返回要映射处理的jdbc类型集合
   */
  JdbcType[] value();

  /**
   * 返回是否映射空值 默认false
   */
  boolean includeNullJdbcType() default false;
}

关于MappedTypes、MappedJdbcTypes的使用,可参考源码测试中的StringTrimmingTypeHandler类:

@MappedTypes(String.class)
@MappedJdbcTypes(value={
   JdbcType.CHAR,JdbcType.VARCHAR}, includeNullJdbcType=true)
public class StringTrimmingTypeHandler implements TypeHandler<String> {
   
  //方法实现ain略
}
TypeReference

TypeReference的核心功能是获取类型转换实现类的父类泛型参数类型,听起来貌似有点绕😊。在转换实现类(比如IntegerTypeHandler)在实例化时,会调用TypeReference的构造函数,而该构造函数中会执行获取父类泛型参数类型的方法getSuperclassTypeParameter()。类的详细说明请参看源码注释说明:

package org.apache.ibatis.type;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public abstract class TypeReference<T> {
   

  //原生类型
  private final Type rawType;

  //构造函数,设置原生类型
  protected TypeReference() {
   
    rawType = getSuperclassTypeParameter(getClass());
  }

  /**
   * 功能描述:根据当前类的Class信息获取超类泛型的参数类型(比如IntegerHandlerType的超类泛型参数为Integer)
   * @param clazz
   * @return
   */
  Type getSuperclassTypeParameter(Class<?> clazz) {
   
    Type genericSuperclass = clazz.getGenericSuperclass();
    //如果传入类的泛型父类为Class的实例且不为TypeReference类,则已clazz的父类为参数递归调用getSuperclassTypeParameter;否则抛出异常
    if (genericSuperclass instanceof Class) {
   
      // try to climb up the hierarchy until meet something useful
      if (TypeReference.class != genericSuperclass) {
   
        return getSuperclassTypeParameter(clazz.getSuperclass());
      }
      throw new TypeException("'" + getClass() + "' extends TypeReference but misses the type parameter. "
        + "Remove the extension or add a type parameter to it.");
    }

    Type rawType = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
    // TODO remove this when Reflector is fixed to return Types
    // 此处貌似说在反射模块中的Reflector修复后会删除如下逻辑(存疑)
    if (rawType instanceof ParameterizedType) {
   
      rawType = ((ParameterizedType) rawType).getRawType();
    }
    return rawType;
  }

  //获取构造方法中设置的原生类型
  public final Type getRawType() {
   
    return rawType;
  }

  //toString方法返回rawType的toString方法
  @Override
  public String toString() {
   
    return rawType.toString();
  }
}
TypeHandler

TypeHandler为类型转换的核心接口,该接口提供四个方法。

package org.apache.ibatis.type;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public interface TypeHandler<T> {
   

  //设置参数
  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  /**
   * @param columnName Colunm name, when configuration <code>useColumnLabel</code> is <code>false</code>
   *  根据ResultSet及columnName获取转换结果
   *  请注意:当configuration中的useColumnLabel=false生效,useColumnLabel默认为true(请参看Configuration中的useColumnLabel属性)
   */
  T getResult(ResultSet rs, String columnName) throws SQLException;

  //根据ResultSet及columnIndex索引获取转换结果
  T getResult(ResultSet rs, int columnIndex) throws SQLException;
  
  //根据CallableStatement及columnIndex索引获取转换结果
  T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
BaseTypeHandler

BaseTypeHandler 该类为抽象类,其继承TypeReference并实现TypeHandler,并且采用模板方法的设计模式,实现了TypeHandler的接口方法的通用逻辑,而相关实现细节则调用定义的抽象方法。由具体的类型转换实现类来实现该方法。

package org.apache.ibatis.type;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.executor.result.ResultMapException;
import org.apache.ibatis.session.Configuration;

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

  /**
   * 设置参数
   * 1、若parameter为空:
   *  1.1、若jdbcType为空,则抛出异常
   *  1.2、ps根据索引位置设置对应的字段为空
   * 2、若parameter不为空,调用非空参数设置方法进行参数设置
   * @param ps
   * @param i
   * @param parameter
   * @param jdbcType
   * @throws SQLException
   */
  @Override
  public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
   
    if (parameter == null) {
   
      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) {
   
        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 {
   
        setNonNullParameter(ps
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值