Mybatis学习(一)--- 类型转换TypeHandler

    Jdbc的类型与java的数据类型并不完全对应,所以在PreparedStatement为sql进行绑定参数的时候,需要从java类型转换为jdbc类型,而从结果集中获取数据的时候,需要将jdbc类型转换为java类型,mybatis使用类处理器解决上述问题。

代码分析

    以java的Integer类型与jdbc对应的int类型进行解析。对应mybatis中的IntegerTypeHandler,其继承关系如下:

TypeHandler接口

    typeHandler接口中定义了四个方法,但是在功能上可以分为两类:

public interface TypeHandler<T> {
  //数据入库之前时数据处理,通过PreparedStatement对象设置SQL参数的时候使用的具体方法,,其中i是参数在SQL的下标,parameter是参数,jdbcType是数据库类型
  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  //下面三个是从数据库加载数据后,vo对象封装前的数据处理,是从JDBC结果集中获取数据进行转换,要么使用列名,(columnname)要么使用下标(columnIndex)获取数据库的数据,其中最后一个getResult方法是存储过程使用的。
  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

    TypeHandler使用了模板设计方法,实现了TypeHandler接口,在baseTypeHandler中实现逻辑的大致框架,具体的处理交由子类实现。讲其中的两个方法的实现。

//当parameter和jdbcType都为空时将抛出异常,如果能明确jdbcType,将会进行空设置(PreparedStatement的setNull方法);如果paramter不为空,将使用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 configuration 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);
    }
  }
}

//非空结果集是通过getNullableResult方法去获取的,该方法需要子类去实现,同样是针对下标、列名以及存储过程三种的实现
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);
  }
}

IntegerTypeHandler

    继承BaseTypeHandler,实现BaseTypeHandler中的抽象方法。代码简单。

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

@Override
//ResultSet获取结果
public Integer getNullableResult(ResultSet rs, String columnName)
    throws SQLException {
  int result = rs.getInt(columnName);
  return result == 0 && rs.wasNull() ? null : result;
}

TypeHandler的注册以及获取

    TypeHandlerRegistry类负责对TypeHandler进行管理,首先先看下mybatis解析XML配置的typeHandlers节点。

for (XNode child : parent.getChildren()) {
  if ("package".equals(child.getName())) {
    String typeHandlerPackage = child.getStringAttribute("name");
    typeHandlerRegistry.register(typeHandlerPackage);
  } else {    
    //子节点为typeHandler时, 可以指定javaType属性, 也可以指定jdbcType, 也可两者都指定
    //javaType 是指定java类型
    //jdbcType 是指定jdbc类型(数据库类型: 如varchar)
    String javaTypeName = child.getStringAttribute("javaType");
    String jdbcTypeName = child.getStringAttribute("jdbcType");
    String handlerTypeName = child.getStringAttribute("handler");
    Class<?> javaTypeClass = resolveClass(javaTypeName);
    JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
    Class<?> typeHandlerClass = resolveClass(handlerTypeName);
    //注册typeHandler, typeHandler通过TypeHandlerRegistry这个类管理
    if (javaTypeClass != null) {
      if (jdbcType == null) {
        typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
      } else {
        typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
      }
    } else {
      typeHandlerRegistry.register(typeHandlerClass);
    }
  }
}

    这个主要是解析XML配置TypeHandler属性,将其交给typeHandlerRegistry进行管理。在mybatis初始化的时候,会为所有已知的类型处理器 TypeHandler 创建对象,同时注册在typeHandlerRegistry上。比如int类型的处理

//在对Integer和int与IntegerTypeHandler注册时,采用java type + handler的形式,注册时首先判断TypeHandler是否有MappedJdbcTypes注解指明jdbc类型
//有则使用java type + jdbc type + handler模式进行三者联合注册,没有则将jdbcType置为null然后进行三者联合注册
register(Integer.class, new IntegerTypeHandler());
register(int.class, new IntegerTypeHandler());
register(JdbcType.INTEGER, new IntegerTypeHandler());

    注册的核心方法:

//java type + jdbc type + handler
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
  if (javaType != null) {
   //javaType对应的【JdbcType和TypeHandler】的映射关系
    Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
    if (map == null || map == NULL_TYPE_HANDLER_MAP) {
      map = new HashMap<>();
      typeHandlerMap.put(javaType, map);
    }
    map.put(jdbcType, handler);
  }
  allTypeHandlersMap.put(handler.getClass(), handler);
}

    注册完成后,TypeHandlerRegistry还提供了查找的方法,可以根据根据 Java 类型和 Jdbc 类型查找对应的 TypeHandler 对象。

TypeAliasRegistry

    mybatis中允许我们使用别名进行注册,TypeAliasRegistry进行别名的管理,其主要是别名的注册和解析,在构造函数中实现通用的别名注册。

public <T> Class<T> resolveAlias(String string) {
  try {
    if (string == null) {
      return null;
    }
    // issue #748
    //转换为小写
    String key = string.toLowerCase(Locale.ENGLISH);
    Class<T> value;
    if (typeAliases.containsKey(key)) {
      value = (Class<T>) typeAliases.get(key);
    } else {
      value = (Class<T>) Resources.classForName(string);
    }
    return value;
  } catch (ClassNotFoundException e) {
    throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
  }
}

自定义注册TypeHandler

    有两种实现方式,一种是实现TypeHandler接口,另一种是继承BaseTypeHandler。我们使用第一种方式实现:

@MappedTypes(String.class)//定义的是JavaType类型,可以指定哪些Java类型被拦截;
@MappedJdbcTypes(JdbcType.BIGINT)//定义的是JdbcType类型,取值范围在枚举类JdbcType中能找到
public class DogTypeHandler implements TypeHandler<String> {
    @Override
    public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        
    }

    @Override
    public String getResult(ResultSet rs, String columnName) throws SQLException {
        return null;
    }

    @Override
    public String getResult(ResultSet rs, int columnIndex) throws SQLException {
        return null;
    }

    @Override
    public String getResult(CallableStatement cs, int columnIndex) throws SQLException {
        return null;
    }
}

    最后在使用的时候,指定typehandler即可。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值