Analytical MyBatis source - type conversion module

Analytical MyBatis source - type conversion module

Foreword

JDBC data type and data type in the Java language is not entirely correspond, so the SQL statement is bound parameters, need to be converted in the PreparedStatement from Java types to JDBC types, and getting data from the result set, you need to type conversion from JDBC to Java types. MyBatis type of processor using the above two types of conversion, as shown in FIG.

In the MyBatis JdbeTypethis data type is an enumeration type represents JIDBC, which defines an enumerated type TYPECODEfield, record the JDBC type javasql.Typesin encoding appropriate constant, and through a static set codelookup( HashMap<nteger,JdbeTypec>type) maintained constant encoding the JdbcType between correspondence.

type Handler

TypeHandlerA processor interface type, MyBatis all types of converters inherit TypeHandlerthe interface, in TypeHandlerthe definition of the following four methods in an interface, the four divided into two methods: setParameter()a method is responsible for converting data into a type JdbeType Javatype; getResult()Method and responsible for its heavy-duty data converted from Java types to JdbcType type.

public interface TypeHandler<T> {

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

  //从 ResultSet 中获取数据时会调用此方法,会将数据由Java类型转换成JdbcType类型
  T getResult(ResultSet rs, String columnName) throws SQLException;

  T getResult(ResultSet rs, int columnIndex) throws SQLException;

  T getResult(CallableStatement cs, int columnIndex) throws SQLException;

}

For the convenience of the user-defined TypeHandler implemented, provided BaseTypeHandler MyBatis abstract class that implements TypeHandlerthe interface and inherits TypeReference abstract class, which inherits the structure shown in FIG.

In the BaseTypeHandlerimplementation TypeHandler.setParameter()method and TypeHandler.getResult()method, the specific implementation is as follows. It should be noted that these two methods for dealing with non-null data are handed over to the sub-class implementation.

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 {
      //绑定参数为null的处理
      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);
    }
  }
}

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);
  }
}

BaseTypeHandlerMore implementation class, as shown, but mostly direct call PreparedStatementand ResultSetor CallableStatementcorresponding method, relatively simple to achieve.

Here for IntegerTypeHandlerexample a brief introduction, other implementation class reader is referred to the relevant source code:

public class IntegerTypeHandler extends BaseTypeHandler<Integer> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
      throws SQLException {
    //调用PreparedStatement.setInt()实现参数绑定
    ps.setInt(i, parameter);
  }

  @Override
  public Integer getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    //调用PreparedStatement.getInt()获取指定列值
    int result = rs.getInt(columnName);
    return result == 0 && rs.wasNull() ? null : result;
  }

  @Override
  public Integer getNullableResult(ResultSet rs, int columnIndex)
      throws SQLException {
    //调用ResultSet.getInt()获取指定列值
    int result = rs.getInt(columnIndex);
    return result == 0 && rs.wasNull() ? null : result;
  }

  @Override
  public Integer getNullableResult(CallableStatement cs, int columnIndex)
      throws SQLException {
    //调用CallableStatement.getInt()获取指定列值
    int result = cs.getInt(columnIndex);
    return result == 0 && cs.wasNull() ? null : result;
  }
}

In general, TypeHandlerfor complete conversion type single column and a single parameter value, if the value into a plurality of rows of Java objects need exists, we should give priority to the use of a suitable definition of mapping rules (in the mapping file <resultMap>to complete the mapping node).

TypeHandlerRegistry

Introduction to TypeHandlerthe interface and its functionality, we look at MyBatishow to manage a large number of TypeHandlerinterface, how to know when to use which TypeHandlerinterface to complete the conversion. These are the TypehandlerRegistrycompletion of the MyBatisinitialization process, will provide all known TypeHandlerobject is created and registered to achieve TypeHandlerRegistryin a TypeHandlerRegistryresponsible hang you these TypeHandlerobjects.

TypeHandlerRegistryThe core meaning of the fields are as follows:

//记录JdbcType与TypeHandler之间的对应关系,其中JdbcType是一个枚举类型,它定义对应的JDBC类型
//该集合主要用于从结果集读取数据时,将数据从Jdbc类型转换成Java类型
private final Map<JdbcType, TypeHandler<?>>  jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
//记录了Java类型向指定JdbcType转换时,需要使用的TypeHandler对象。例如: Java 类型中的String可能
//转换成数据库的char、varchar等多种类型,所以存在一对多关系
private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
private final TypeHandler<Object> unknownTypeHandler;
//记录了全部TypeHandler类型以及该类型相应的TypeHandler对象
private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();

//空TypeHandler集合的标识
private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();

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

1. Registration TypeHandlerObject

TypeHandlerRegistry.register()Sign TypeHandler way to achieve the object function, the registration process will be added to the four sets of TypeHandlerobjects. register()The method has a plurality of overload, a relationship between the call shown in FIG overloads.

As can be seen from the figure, most of the register () method will eventually call the overloaded ④ complete the registration function, we first introduce the method to achieve its three parameters specify the type TypeHandler Java that can be handled, Jdbc type objects and TypeHandler .

//register()方法的重载④的实现如下:
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
  if (javaType != null) { //检验是否明确指定了TypeHandler能够处理的Java类型
    //获取指定Java类型在TYPE_ HANDLER_ MAP集合中对应的TypeHandler集合
    Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
    if (map == null || map == NULL_TYPE_HANDLER_MAP) {
      //创建新的TypeHandler集合,并添加到TYPE HANDLER MAP中
      map = new HashMap<>();
      typeHandlerMap.put(javaType, map);
    }
    //将TypeHandler对象注册到typeHandlerMap集合中
    map.put(jdbcType, handler);
  }
  //向AllTypeHandler集合注册TypeHandler类型和对应的TypeHandler对象【以Class为key handler对象为value】
  allTypeHandlersMap.put(handler.getClass(), handler);
}

In this three ① ~ ③ registr()method overloading will try to read TypeHandlerthe class defined in the @MappedTypesannotations and @MappedJdbcTypesnotes, @MappedTypesannotations for specifying the TypeHandlerset of Java class type that can be processed to achieve, @MappedJdbcTypesannotations for the specified TypeHandlerimplementation class can be processed by JDBCa set of type . register()Overload ① ~ ③ specific method is as follows:

//register()方法的重载①的实现如下:
public void register(Class<?> typeHandlerClass) {
boolean mappedTypeFound = false;
//获取 @MappedTypes 注解
MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
if (mappedTypes != null) {
  for (Class<?> javaTypeClass : mappedTypes.value()) {
    //经过强制类型转换 以及反射创建TypeHandler对象之后,交由重载③继续处理
    register(javaTypeClass, typeHandlerClass);
    mappedTypeFound = true;
  }
}
if (!mappedTypeFound) {
  //未@MappedTypes 注解,调用重载方法 ② 处理
  register(getInstance(null, typeHandlerClass));
}
}
//register()方法的重载②的实现如下:
public <T> void register(TypeHandler<T> typeHandler) {
boolean mappedTypeFound = false;
//获取@MappedTypes注解 并根据MappedTypes注解指定的java类型进行注册,逻辑与重载①类似
MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);
if (mappedTypes != null) {
  for (Class<?> handledType : mappedTypes.value()) {
    //交由重载③进行处理
    register(handledType, typeHandler);
    mappedTypeFound = true;
  }
}
//从3.1.0版本开始,可以根据TypeHandler类型自动查找对应的Java类型,这需要
//我们的TypeHandler实现类同时继承TypeReference这个抽象类
// @since 3.1.0 - try to auto-discover the mapped type
if (!mappedTypeFound && typeHandler instanceof TypeReference) {
  try {
    TypeReference<T> typeReference = (TypeReference<T>) typeHandler;
    //交由重载③进行处理
    register(typeReference.getRawType(), typeHandler);
    mappedTypeFound = true;
  } catch (Throwable t) {
    // maybe users define the TypeReference with a different type and are not assignable, so just ignore it
  }
}
if (!mappedTypeFound) {
  //类型转换后,交由重载③继续处理
  register((Class<T>) null, typeHandler);
}
}
//register()方法的重载③的实现如下:
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
//获取MappedJdbcTypes注解
MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
if (mappedJdbcTypes != null) {
  //根据指定类型进行注册 读取@MappedJdbcTypes value值 获取handlerJdbcType
  for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
    register(javaType, handledJdbcType, typeHandler);
  }
  if (mappedJdbcTypes.includeNullJdbcType()) {
    register(javaType, null, typeHandler);
  }
} else {
  //未指定MappedJdbcTypes注解 交由重载④继续处理
  register(javaType, null, typeHandler);
}
}

All the above register()methods are overloaded in the TYPE_ HANDLER_ _MAPset and ALL_TYPE_HANDLERS MAPregistered TypeHandler object set is to be overloaded to ⑤ JDBC_ TYPE_ HANDLER_ MAPcollection registered TypeHandlersubject, specific implementation is as follows:

//register()方法重载⑤的实现如下:
public void register(JdbcType jdbcType, TypeHandler<?> handler) {
// 注册JdbcType类型对一个的TypeHandler
jdbcTypeHandlerMap.put(jdbcType, handler);
}

TypeHandlerRegistryIn addition to providing a single registration TypeHandlerof register()overloaded and can also scan the entire packet TypeHandlerinterface classes, and complete the TypeHandlerregistration implementation class. Let's look at a concrete realization of heavy-duty ⑥:

//register()方法重载⑥的实现,主要用来自动扫描指定包下的TypeHandler实现并完成注册
public void register(String packageName) {
  ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
  //查找指定包下的TypeHandler接口实现类
  resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
  Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
  for (Class<?> type : handlerSet) {
    //Ignore inner classes and interfaces (including package-info.java) and abstract classes
    //过滤调内部类、接口以及抽象类
    if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
      //交由重载①继续后面注册操作
      register(type);
    }
  }
}

Finally view TypeHandlerRegistryconstructor will by the above register()method is the basis of many types registered corresponding TypeHandlerobject code is briefly as follows:

public TypeHandlerRegistry(Configuration configuration) {
    //...这里重点看下String相关的TypeHandler对象的注册 其他类型的TypeHandler的注册类型(略)
    // stringTypeHandler 能够将数据从String 类型转换成null ( JdbcType ),所以向
    TYPE HANDLER MAP
    //集合注册该对象,并向ALL_ TYPE_ HANDLERS_ MAP集合注册StringTypeHandler
    register(String.class, new StringTypeHandler());
    
    register(JdbcType.NVARCHAR, new NStringTypeHandler());
    //向JDBC_TYPE_HANDLER_MAP集合注册NVARCHAR对应的NStringTypeHandler
  register(JdbcType.NVARCHAR, new NStringTypeHandler());
  // ...注册其他JdbcType类型对应的TypeHandler的过程类似(略)
}

2. Find TypeHandler objects

After introducing registration TypeHandler object function, come to discover a TypeHandler object function TypeHandlerRegistry provided. TypeHandlerRegistry.getTypeHandler () method to achieve the object of obtaining a corresponding TypeHandler from said set of four features. TypeHandlerRegistry.getTypeHandler () method of a plurality of overload, the relationship between these overloads shown in FIG.

After a series of casts, TypeHandlerRegistry.getTypeHandler()multiple overloaded method will call TypeHandlerRegistry.getTypeHandle(Type,JdbcType)the overloaded method, it will be according to the specified Javatype and JdbcTypefind the appropriate type of TypeHandlerobjects, specifically to achieve the following:

private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
  if (ParamMap.class.equals(type)) {
    return null;
  }
  //查找或初始化Java类型对一个的TypeHandler对象
  Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
  TypeHandler<?> handler = null;
  if (jdbcHandlerMap != null) {
    //根据JdbcType查找TypeHandler
    handler = jdbcHandlerMap.get(jdbcType);
    if (handler == null) {
      handler = jdbcHandlerMap.get(null);
    }
    if (handler == null) {
      // #591
      //如果JdbcType只注册了一个TypeHandler,使用此TypeHandler对象
      handler = pickSoleHandler(jdbcHandlerMap);
    }
  }
  // type drives generics here
  return (TypeHandler<T>) handler;
}

In the TypeHandlerRegitry.getJdbcHandlerMap()process, it detects TYPE_HANDLER_MAPthe collection specified Javatype corresponding to TypeHandlerwhether the set has been initialized. If not initialized, then the attempt to the Java type, initialized parent class corresponding to TypeHandlerthe set of the initial set; already initialized as parent does not exist, the corresponding TypeHandlerset is initialized to NULL_TYPE_HANDLER_MAPidentity. getJdbcHandlerMap()DETAILED implemented as follows:

private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMap(Type type) {
    //查找指定Java类型对应的TypeHandler集合
    Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(type);
    if (NULL_TYPE_HANDLER_MAP.equals(jdbcHandlerMap)) { //检测是否为空集合标识
      return null;
    }
    //初始化指定的Java类型的TypeHandler集合
    if (jdbcHandlerMap == null && type instanceof Class) {
      Class<?> clazz = (Class<?>) type;
      if (Enum.class.isAssignableFrom(clazz)) {
        Class<?> enumClass = clazz.isAnonymousClass() ? clazz.getSuperclass() : clazz;
        jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(enumClass, enumClass);
        if (jdbcHandlerMap == null) {
          //枚举类型的处理
          register(enumClass, getInstance(enumClass, defaultEnumTypeHandler));
          return typeHandlerMap.get(enumClass);
        }
      } else {
        //查找父类对应的TypeHandler集合,并作为初始化集合
        jdbcHandlerMap = getJdbcHandlerMapForSuperclass(clazz);
      }
    }
    // 以NULL_TYPE_HANDLER_MAP作为TypeHandler集合
    typeHandlerMap.put(type, jdbcHandlerMap == null ? NULL_TYPE_HANDLER_MAP : jdbcHandlerMap);
    return jdbcHandlerMap;
  }
  
  // getJdbcHandlerMapForSuperclass()实现
  private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForSuperclass(Class<?> clazz) {
    Class<?> superclass =  clazz.getSuperclass();
    if (superclass == null || Object.class.equals(superclass)) {
      return null;  //父类为Object或null则查找结束
    }
    Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(superclass);
    if (jdbcHandlerMap != null) {
      return jdbcHandlerMap;
    } else {
      //继续递归查找父类对应的TypeHandler集合
      return getJdbcHandlerMapForSuperclass(superclass);
    }
  }

TypeHandlerRegistry.getMappingTypeHandler()The method will, from the specified type directly TypeHandler ALL_TYPE_HANDLERS_MAPfind the collection TypeHandlerobjects. TypeHandlerRegistry.getTypeHandler(JdbcType)The method will be based on specific JdbcType, from the type JDBC_TYPE_HANDLER_MAPto find the collection TypeHandlerobjects. These two relatively simple method, the code is not posted.
Finally, in addition to MyBatisitself provides the TypeHandlerimplementation, we can also add a custom TypeHandler
interface, in the manner of adding mybatisconfig.xmlthe configuration file <typeHandlers>at the node, add the appropriate <typeHandler>node configuration, and specify a custom TypeHandlerimplementation of that interface. In the MyBatisinitialization parses the node, and the TypeHandlerobject type is registered TypeHandlerRegistryin, for MyBatissubsequent use. Described later MyBatiswill be referred to the initialization configuration.

TypeAliasRegistry

When writing SQL statements using aliases can easily understand and maintain, for example, table or column names long, we generally easy to understand and maintain their aliases design. MyBatisThe SQL statement alias extension and expansion of the concept, MyBatiscan add a an alias as a class, then the class can be referenced by alias. MyBatisBy TypeAliasRegistrythe completion of registration and management of the alias functional class, TypeAliasRegistryrelatively simple structure, by which TYPE_ ALIASESfield ( Map<String, Class<?>>management type alias) Javathe correspondence relationship between the type, by TypeAliasRegistry.registerAlias()the method alias complete the registration, the specific implementation method is as follows:

public void registerAlias(String alias, Class<?> value) {
  if (alias == null) {  //校验别名是否为空,为空则抛出类型异常
    throw new TypeException("The parameter alias cannot be null");
  }
  // issue #748
  //将别名转换为小写
  String key = alias.toLowerCase(Locale.ENGLISH);
  //校验别名是否已经存在
  if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
    throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
  }
  //注册别名
  typeAliases.put(key, value);
}

In the TypeAliasRegistryconstruction process, the default is the Basic Type and Java array type, base type and array type wrapper classes, Date, BigDecimal, BigInteger, Map, HashMap, List, ArrayList, Collection, Iterator, ResultSetand other types of add an alias, code is relatively simple, the reader is referred TypeAliasRegistryto Source learning.

TypeAliasRegistryThere are two methods need to explain, registerAliases(String,Class<?>)heavy-duty package will scan all the specified class, and add the alias as a subclass of the specified class; registerAlias(Class<?>)overload will try to read @Aliasnotes. Achieve these two methods as follows:

public void registerAliases(String packageName, Class<?> superType) {
  ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
  //查找指定包下的superType类型类
  resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
  Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
  for (Class<?> type : typeSet) {
    // Ignore inner classes and interfaces (including package-info.java)
    // Skip also inner classes. See issue #6
    //过滤调内部类、接口、抽象类
    if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
      registerAlias(type);
    }
  }
}

public void registerAlias(Class<?> type) {
  String alias = type.getSimpleName();  //类型的简单名称(不包含报名)
  //读取Alias注解
  Alias aliasAnnotation = type.getAnnotation(Alias.class);
  if (aliasAnnotation != null) {
    alias = aliasAnnotation.value();
  }
  //检测次别名不存在后,会将其记录到TYPE_ALIASES集合中
  registerAlias(alias, type);
}

This article authored by Janker, using CC BY 3.0 CN agreement to license. It can be freely reproduced, quoted with named authors and indicate the source of the article. Reprinted as to micro-channel public number, add the number of the public in the end the two-dimensional code.

Guess you like

Origin www.cnblogs.com/liuzhiyong0718/p/12467253.html