fuente MyBatis Analytical - módulo de conversión de tipo

fuente MyBatis Analytical - módulo de conversión de tipo

prefacio

tipo de datos JDBC y tipo de datos en el lenguaje Java no es del todo corresponden, por lo que la instrucción SQL es parámetros vinculados, tienen que convertirse en el PreparedStatement de tipos Java a tipos JDBC, y la obtención de datos del conjunto de resultados, lo que necesita para Conversión de tipo de JDBC tipos de Java. MyBatis tipo de procesador utilizando los dos tipos de conversión anterior, como se muestra en la figura.

En los MyBatis JdbeTypeeste tipo de datos es un tipo de enumeración representa JIDBC, que define un tipo enumerado TYPECODEcampo, registrar el tipo JDBC javasql.Typesen la codificación constante apropiada, y a través de un conjunto estático codelookup( HashMap<nteger,JdbeTypec>tipo) mantiene constante que codifica la jdbcType entre la correspondencia.

tipo de controlador

TypeHandlerUn tipo de interfaz del procesador, mybatis todos los tipos de convertidores heredan TypeHandlerla interfaz, en TypeHandlerla definición de los cuatro métodos siguientes en una interfaz, los cuatro divididos en dos métodos: setParameter()un método es responsable de convertir los datos en un tipo JdbeType Javatipo; getResult()Método y responsable de sus datos pesados convertidos a partir de tipos de Java para el tipo jdbcType.

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;

}

Para la conveniencia del usuario define-TypeHandler implementado, siempre BaseTypeHandler MyBatis clase abstracta que implementa TypeHandlerla interfaz y hereda typereference clase abstracta, que hereda la estructura mostrada en la figura.

En la BaseTypeHandleraplicación TypeHandler.setParameter()método y TypeHandler.getResult()método, la aplicación concreta es la siguiente. Cabe señalar que estos dos métodos para tratar con datos no nulos son entregados a la implementación de sub-clase.

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

BaseTypeHandlerMás clase de implementación, como se muestra, pero sobre todo la llamada directa PreparedStatementy ResultSeto CallableStatementcorrespondiente método, relativamente fácil de lograr.

Aquí por IntegerTypeHandlerejemplo una introducción breve, otro lector clase de implementación se conoce el código fuente correspondiente:

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

En general, TypeHandlerpara la completa tipo de conversión sola columna y un solo valor de parámetro, si el valor en una pluralidad de filas de objetos Java existe necesidad, debemos dar prioridad a la utilización de una definición adecuada de las reglas de asignación (en el archivo de asignación <resultMap>para completar el nodo de mapeo).

TypeHandlerRegistry

Introducción a TypeHandlerla interfaz y su funcionalidad, nos fijamos en MyBatisla forma de gestionar un gran número de TypeHandlerinterfaz, cómo saber cuándo usar cada TypeHandlerinterfaz para completar la conversión. Estos son los TypehandlerRegistryfinalización del MyBatisproceso de inicialización, proporcionará a todos los conocidos TypeHandlerobjeto se crea y se registró para lograr TypeHandlerRegistryen un TypeHandlerRegistryresponsable que colgar estos TypeHandlerobjetos.

TypeHandlerRegistryEl núcleo significado de los campos son los siguientes:

//记录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. Registro TypeHandlerde objetos

TypeHandlerRegistry.register()Inscripción manera TypeHandler para lograr la función del objeto, el proceso de registro se añadirá a los cuatro conjuntos de TypeHandlerobjetos. register()El método tiene una pluralidad de sobrecarga, una relación entre la llamada se muestra en la FIG sobrecargas.

Como puede verse en la figura, la mayor parte del método de registro () finalmente llamará a la sobrecarga ④ completar la función de registro, primero introducimos el método para lograr sus tres parámetros especifican el tipo TypeHandler Java que puede ser manejado, objetos de tipo JDBC y 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);
}

En este tres ① ~ ③ registr()sobrecarga de métodos intentará leer TypeHandlerla clase definida en las @MappedTypesanotaciones y @MappedJdbcTypesnotas, @MappedTypesanotaciones para especificar el TypeHandlerconjunto de Java tipo de clase que puede ser procesada para lograr, @MappedJdbcTypesanotaciones con el objetivo específico TypeHandlerde clase de implementación pueden ser procesados por JDBCun conjunto de tipo . register()Sobrecarga ① ~ ③ método específico es el siguiente:

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

Todos los anteriores register()métodos están sobrecargados en el TYPE_ HANDLER_ _MAPconjunto y ALL_TYPE_HANDLERS MAPconjunto de objetos de TypeHandler registrada debe ser sobrecargado para ⑤ JDBC_ TYPE_ HANDLER_ MAPcolección registrado TypeHandlersujeto, de implementación específico es el siguiente:

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

TypeHandlerRegistryAdemás de proporcionar un único registro TypeHandlerde register()sobrecarga y también puede escanear todo el paquete de TypeHandlerclases de interfaz, y completar la TypeHandlerclase de implementación de registro. Echemos un vistazo a una realización concreta de trabajo pesado ⑥:

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

Finalmente ver TypeHandlerRegistryconstructor voluntad por el anterior register()método es la base de muchos tipos registrado correspondiente TypeHandlercódigo objeto es brevemente como sigue:

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. Encontrar objetos TypeHandler

Después de la introducción de la función objetivo TypeHandler registro, vienen a descubrir una función TypeHandlerRegistry objeto TypeHandler proporcionado. método TypeHandlerRegistry.getTypeHandler () para lograr el objeto de obtener un TypeHandler correspondiente de dicho conjunto de cuatro características. método TypeHandlerRegistry.getTypeHandler () de una pluralidad de sobrecarga, la relación entre estas sobrecargas muestra en la figura.

Después de una serie de moldes, TypeHandlerRegistry.getTypeHandler()método sobrecargado múltiple llamará TypeHandlerRegistry.getTypeHandle(Type,JdbcType)el método sobrecargado, que será de acuerdo a la especificada Javatipo y JdbcTypeencontrar el tipo apropiado de TypeHandlerobjetos, específicamente para lograr lo siguiente:

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

En el TypeHandlerRegitry.getJdbcHandlerMap()proceso, se detecta TYPE_HANDLER_MAPel especificado colección Javatipo correspondiente a TypeHandlersi el conjunto se ha inicializado. Si no inicializado, entonces el intento de la clase Java, inicializado clase padre que corresponde a TypeHandlerla serie de la primera serie, que ya está iniciado como no existe padres, el correspondiente TypeHandlerconjunto es inicializado a NULL_TYPE_HANDLER_MAPla identidad. getJdbcHandlerMap()Detalladas implementado como sigue:

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()El método será, a partir del tipo especificado directamente TypeHandler ALL_TYPE_HANDLERS_MAPencontrar la colección TypeHandlerde objetos. TypeHandlerRegistry.getTypeHandler(JdbcType)El método se basa en específico JdbcType, a partir del tipo JDBC_TYPE_HANDLER_MAPde encontrar la colección TypeHandlerde objetos. Estos dos método relativamente simple, el código no se ha publicado.
Por último, además de MyBatissí mismo proporciona la TypeHandleraplicación, también podemos añadir un encargo de TypeHandler
interfaz, a la manera de añadir mybatisconfig.xmlel archivo de configuración <typeHandlers>en el nodo, agregue la adecuada <typeHandler>configuración de los nodos, y especificar una costumbre TypeHandleraplicación de esa interfaz. En la MyBatisinicialización analiza el nodo, y el TypeHandlertipo de objeto se ha registrado TypeHandlerRegistryen, por MyBatisuso posterior. Se describe más adelante MyBatisse hará referencia a la configuración de inicialización.

TypeAliasRegistry

Al escribir sentencias SQL utilizando alias puede entender fácilmente y mantener, por ejemplo, nombres de tabla o columna larga, por lo general, fácil de entender y mantener su diseño alias. MyBatisLa extensión de alias instrucción SQL y la expansión del concepto, MyBatisse puede añadir un alias como una clase, entonces la clase se puede hacer referencia por alias. MyBatisPor TypeAliasRegistryla terminación de la inscripción y gestión de la clase funcional de alias, TypeAliasRegistryestructura relativamente simple, por el que TYPE_ ALIASESel campo ( Map<String, Class<?>>tipo de gestión alias) Javala relación de correspondencia entre el tipo, por TypeAliasRegistry.registerAlias()los alias método completo el registro, el método de implementación específico es el siguiente:

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

En el TypeAliasRegistryproceso de construcción, el valor predeterminado es el tipo básico y Java tipo de matriz, tipo de base y tipo de matriz envoltorio clases, Date, BigDecimal, BigInteger, Map, HashMap, List, ArrayList, Collection, Iterator, ResultSety otros tipos de agregar un alias, el código es relativamente simple, se remite al lector TypeAliasRegistrya fuente de aprendizaje.

TypeAliasRegistryHay dos métodos tienen que explicar, registerAliases(String,Class<?>)paquete de servicio pesado escaneará toda la clase especificada, y añadir el alias como una subclase de la clase especificada; registerAlias(Class<?>)sobrecarga intentará leer @Aliasnotas. Lograr estos dos métodos como sigue:

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

Este artículo escrito por Janker, utilizando CC BY 3.0 CN acuerdo a la licencia. Puede ser reproducido libremente, citando a los autores nombrados e indicar el origen del artículo. Reproducido en cuanto a número público de micro-canales, añadir el número del público al final el código de dos dimensiones.

Supongo que te gusta

Origin www.cnblogs.com/liuzhiyong0718/p/12467253.html
Recomendado
Clasificación