Mybatis—TypeHandler

  Mybatis在执行SQL语句之前和完成之后都将要处理JDBC类型与Java类型之间的转换,简言之TypeHandler主要的工作就是完成JDBC类型和Java类型之间的相互转换,具体情况如下:

  1. 执行SQL语句之前为PreparedStatement对象参数占位符赋值,具体调用PreparedStatement接口中提供的一系列setter方法,将Java类型转换为对应的JDBC类型;
  2. 执行完成SQL语句获取到ResultSet对象之后,调用ResultSet提供的getter方法将JDBC类型转换为Java类型。
    在这里插入图片描述

  TypeHandler使用模板方法设计模式,TypeHandler接口定义了相关设置参数和获取执行SQL语句完成后结果的一些方法,BaseTypeHandler抽象类实现了TypeHandler接口定义方法的默认实现,并重新定义了赋值和取值的方法,其实现逻辑交给具体实现类来实现。Mybatis提供了常规数据类型的TypeHandler,如果想要实现自定义TypeHandler,通过继承BaseTypeHandler抽象类即可。
  Mybatis中默认实现了很多TypeHandler,如StringTypeHandler、BigDecimalTypeHandler和BooleanTypeHandler等TypeHandler。从BigDecimalTypeHandler源代码可以看出,BigDecimalTypeHandler实现了BaseTypeHandler定义了四个抽象方法,BigDecimalTypeHandler#setNonNullParameter()方法调用PreparedStatement对象的setBigDecimal()方法将BigDecimal类型转换为对应的JDBC类型,并为参数占位符赋值。getNullableResult()方法调用ResultSet对象的getBigDecimal()方法将JDBC中的字符串类型转为BigDecimal类型,并返回列的值。

public class BigDecimalTypeHandler extends BaseTypeHandler<BigDecimal> {

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

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

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

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

TypeHandlerRegistry

  TypeHandlerRegistry定义了JDBC类型、Java类型与TypeHandler之间的映射关系,如下源代码所示,

public final class TypeHandlerRegistry {

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

  private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = 
    				Collections.emptyMap();

  private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;
}

TypeHandlerRegistry使用Map数据结构来定义了JDBC类型、Java类型与TypeHandler之间的关系,分别使用jdbcTypeHandlerMap和typeHandlerMap属性存储对应的关系定义。
  TypeHandlerRegistry以构造方法的形式来完成默认JDBC类型、Java类型与TypeHandler关系的定义,并实现getTypeHandler和hasTypeHandler方法对外提供根据Java类型或者JDBC类型获取对应的TypeHandler对象,以及判断Java类型是否有对应的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());
    // 省略其它代码
}

实例

  以DefaultParameterHandler#setParameters为例,当通过SimpleExecutor类doQuery方法完成SQL语句执行之前,会先调用DefaultParameterHandler类的setParameters方法来完成SQL语句中占位符参数的赋值。

@Override
public void setParameters(PreparedStatement ps) {
    ErrorContext.instance()
    	.activity("setting parameters")
    	.object(mappedStatement.getParameterMap().getId());
	// 获取SQL中的占位符参数定义
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { 
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            // 如果类型处理器中有对应的typeHandler,直接赋值
            value = parameterObject;
          } else {
            // 如果没有对应的typeHandler,将参数转化为MetaObject反射工具类
            // 然后从元数据类中寻找当前参数的值
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          // 获取typeHandler类型处理器
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          // 获取JdbcType Java类型定义
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            // 调用typeHandler的setParameter方法完成占位符参数的赋值
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException | SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
}

  DefaultParameterHandler#setParameters源代码虽然篇幅很大,但是其核心逻辑并不复杂:

  1. 从BoundSql类中获取当前SQL语句中的占位符参数ParameterMapping定义,如果不为空就直接遍历ParameterMapping定义,为每一个占位符参数赋值;
  2. 如果占位符参数的ParameterMode不是OUT就进行赋值处理;
  3. 如果在类型处理器中找到对应的typeHandler,就直接赋值;
  4. 如果在没有找到对应的typeHandler,将参数转化为MetaObject反射工具类,然后从元数据类中寻找当前参数的值;
  5. 得到value值之后,再获取在ParameterMapping中定义的typeHandler类型处理器和Java类型;
  6. 调用typeHandler类型处理器的setParameter方法,将value值设置到当前的PreparedStatement对象。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乐只乐之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值