mybatis底层组件介绍-TypeHandler和TypeHandlerRegistry

本文详细介绍了MyBatis中的TypeHandler机制,包括其作用、接口定义、基类BaseTypeHandler的实现以及特定类型如BooleanTypeHandler的处理方式。TypeHandler在Java数据类型与JDBC数据类型之间起着转换桥梁的作用,用于参数设置和结果获取。此外,还探讨了TypeHandler的注册过程和获取方式,展示了如何通过TypeHandlerRegistry进行类型处理器的注册和查找。

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

TypeHandler

因为jdbc中数据的类型和java的数据类型并不是直接对应的,因此需要在jdbc数据类型和java数据类型之间进行转换,转换主要发生在两个场景:

  1. 当给sql语句注入参数时,需要将java数据类型转换成jdbc数据类型
  2. 当从数据库中查询出记录后,需要将对应的记录转换成DO时,需要将jdbc数据类型转换成jdbc数据类型
    而TypeHandler就是用来进行jdbc数据类型和java数据类型之间的转换的

接口方法

首先看下TypeHandler这个接口都有哪些方法

public interface TypeHandler<T> {

	// 将参数从java数据类型转化成jdbc数据类型,然后绑定到sql上
  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  /**
   * Gets the result.
   *
   * @param rs
   *          the rs
   * @param columnName
   *          Colunm name, when configuration <code>useColumnLabel</code> is <code>false</code>
   * @return the result
   * @throws SQLException
   *           the SQL exception
   */
   /**
   	* 下面的多个方法将从数据库中读取的列数据转换成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;
}

BaseTypeHandler

BaseTypeHandler是实现了TypeHandler接口的一个简单抽象类
下面看下它是如何实现方法的

setParameter

setParameter的实现中完成了对null值的处理,将非null值的处理方法抽到setNonNullParameter中,由子类来实现

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 configuBaration property. "
            + "Cause: " + e, e);
    }
  } else {
    try {
      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);
    }
  }
}

getResult

只是简单地对异常进行了一次转换,将具体的实现抽到getNullableResult中,由子类来实现

@Override
public T getResult(ResultSet rs, String columnName) throws SQLException {
  try {
    return getNullableResult(rs, columnName);
  } catch (Exception e) {
    throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);
  }
}

子类BooleanTypeHandler

这里简单地看下子类BooleanTypeHandler,比较简单,主要就是调用PreparedStatement的方法

public class BooleanTypeHandler extends BaseTypeHandler<Boolean> {

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

  @Override
  public Boolean getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    boolean result = rs.getBoolean(columnName);
    return !result && rs.wasNull() ? null : result;
  }

  @Override
  public Boolean getNullableResult(ResultSet rs, int columnIndex)
      throws SQLException {
    boolean result = rs.getBoolean(columnIndex);
    return !result && rs.wasNull() ? null : result;
  }

  @Override
  public Boolean getNullableResult(CallableStatement cs, int columnIndex)
      throws SQLException {
    boolean result = cs.getBoolean(columnIndex);
    return !result && cs.wasNull() ? null : result;
  }
}

TypeHandlerRegister

TypeHandlerRegister用来注册TypeHandler

构造函数

public TypeHandlerRegistry(Configuration configuration) {
    this.unknownTypeHandler = new UnknownTypeHandler(configuration);

    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());
    // 省略下面的,都是对一些handler的注册

构造函数中主要就是注册一些默认的TypeHandler
另外需要看几个比较重要的map

private final Map<JdbcType, TypeHandler<?>>  jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();

上面的三个map具体是干嘛的,通过他们的泛型和变量名称非常容易理解,下面通过register来注册typehandler的时候,会更新这些map

register

基于java类型的注册

可以看到有两个版本的register,一种是需要指定jdbc数据类型的,另外一种则是不需要指定jdbc类型的
当调用了不需要指定Jdbc类型版本时,会判断注册的handler上是否使用了MappedJdbcTypes注解,如果使用了该注解,那么会读取该注解的配置信息,从而解析出当前需要注册的jdbc数据类型

public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler) {
  register((Type) javaType, typeHandler);
}

private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
  // 从typehandler的MappedJdbcTypes注解上读取当前需要注册的jdbc数据类型
  MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
  if (mappedJdbcTypes != null) {
    for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
      register(javaType, handledJdbcType, typeHandler);
    }
    if (mappedJdbcTypes.includeNullJdbcType()) {
      register(javaType, null, typeHandler);
    }
  } else {
    register(javaType, null, typeHandler);
  }
} 
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
  if (javaType != null) {
    Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
    if (map == null || map == NULL_TYPE_HANDLER_MAP) {
      map = new HashMap<>();
    }
    map.put(jdbcType, handler);
    typeHandlerMap.put(javaType, map);
  }
  allTypeHandlersMap.put(handler.getClass(), handler);
}

另外如果同时指定了jdbc类型和java类型,那么调用的也是这里的方法

基于jdbc类型的注册

public void register(JdbcType jdbcType, TypeHandler<?> handler) {
 jdbcTypeHandlerMap.put(jdbcType, handler);
}

getTypeHandler

getTypeHandler主要是根据传入的参数来获取相应的typehandler
getTypeHandler有多个重载版本,但是最终都会调用下面这个版本

private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
 if (ParamMap.class.equals(type)) {
   return null;
 }
 // 获取当前java类型的用来获取转换到不同jdbctype的handler的map
 Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
 TypeHandler<?> handler = null;
 if (jdbcHandlerMap != null) {
   handler = jdbcHandlerMap.get(jdbcType);
   if (handler == null) {
     handler = jdbcHandlerMap.get(null);
   }
   if (handler == null) {
     // #591
     // 如果只注册了一个typehandler,那么会返回这个typehandler
     handler = pickSoleHandler(jdbcHandlerMap);
   }
 }
 // type drives generics here
 return (TypeHandler<T>) handler;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值