美文网首页MyBatis源码剖析
[MyBatis源码分析 - 类型模块 - 组件二] TypeH

[MyBatis源码分析 - 类型模块 - 组件二] TypeH

作者: 小胡_鸭 | 来源:发表于2020-11-01 00:08 被阅读0次

一、简介

  TypeHandlerRegistry 类型处理器注册表注册了类型转换时需要用到的各种处理器以及与Java类型和Jdbc类型的映射关系。

二、数据结构

  // 记录JdbcType和TypeHandler之间的对应关系,其中JdbcType是一个枚举类型,它定义对应的JDBC类型
  // 该集合主要用于从结果集读取数据时,将数据从Jdbc类型转换成Java类型
  private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);

  // 记录了Java类型向指定的JdbcType转换时,需要使用的TypeHandler对象。例如:Java类型中的String可能
  // 转换成数据库的char、varchar等多种类型,所以存在一对多的关系,所以值要用Map来存储
  private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<Type, Map<JdbcType, TypeHandler<?>>>();

  // 未知类型对象的TypeHandler
  private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);

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

三、getInstance

【功能】根据传入的类型处理器类型和指定处理的Java类型,创建类型处理器实例对象。
【源码与注解】

  public <T> TypeHandler<T> getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
    // 如果传入的Java类型非空,则使用处理器类型的含参构造器创建处理器
    if (javaTypeClass != null) {
      try {
        Constructor<?> c = typeHandlerClass.getConstructor(Class.class);
        return (TypeHandler<T>) c.newInstance(javaTypeClass);
      } catch (NoSuchMethodException ignored) {
        // ignored
      } catch (Exception e) {
        throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e);
      }
    }
    // 如果传入的Java类型为空,则使用处理器类型的默认构造器创建处理器
    try {
      Constructor<?> c = typeHandlerClass.getConstructor();
      return (TypeHandler<T>) c.newInstance();
    } catch (Exception e) {
      throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e);
    }
  }

四、处理器注册

  TypeHandlerRegistry 中主要有两类方法,其中一类就是根据各种入参注册处理器,类中有很多 #registry 方法的重载实现,其调用关系如下图所示:


  从图中可以看出除了方法 ⑤ ,最终都会调用到方法④ ,所以先介绍方法④ ,源码如下:
  private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
    // (1)添加 handler 到 TYPE_HANDLER_MAP 中
    if (javaType != null) {
      Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
      if (map == null) {
        map = new HashMap<JdbcType, TypeHandler<?>>();
        TYPE_HANDLER_MAP.put(javaType, map);
      }
      map.put(jdbcType, handler);
    }
    // (2)添加 handler 到 ALL_TYPE_HANDLERS_MAP
    ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
  }

【解析】

  • (1)若传入的 Java 类型非空,考虑到可能该 Java 类型已经注册了跟某个 JdbcType 映射处理时用到的处理器,所以先尝试从 TYPE_HANDLER_MAP 集合中找到对应的映射map,若为空表示第一次注册与该 Java 类型处理相关的处理器,则先创建map,再添加到 TYPE_HANDLER_MAP` 集合中。
  • (2)若传入的 Java 类型为空,则只注册 handler 到 ALL_TYPE_HANDLERS_MAP 集合中。

⑥ register(String packageName)

【功能】通过包名,批量注册类型处理器,当在 mybatis-config.xml 通过包扫描的方式注册处理器时就会调用本方法处理。
【源码与注解】

  public void register(String packageName) {
    // 解析器工具类: 从指定包名中解析出包中的类型处理器
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    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
      // 过滤掉内部类、接口以及抽象类,调用 #register(Class<?> typeHandlerClass) 注册
      if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
        register(type);
      }
    }
  }

【解析】
  ResolverUtil 是一个用来解析包中指定类型的工具类,解析后会保存到其对象内部的一个集合中,这里解析完之后对该集合进行了遍历,循环体内先过滤掉内部类、接口和抽象类,然后调用 #register(Class<?> typeHandlerClass) 方法处理。

① register(Class<?> typeHandlerClass)

  public void register(Class<?> typeHandlerClass) {
    boolean mappedTypeFound = false;
    // (1)获得 @MappedTypes 注解
    MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
    if (mappedTypes != null) {
      for (Class<?> javaTypeClass : mappedTypes.value()) {
        register(javaTypeClass, typeHandlerClass);
        mappedTypeFound = true;
      }
    }
    // (2)未使用 @MappedTypes 注解,直接使用
    if (!mappedTypeFound) {
      register(getInstance(null, typeHandlerClass));
    }
  }

【解析】

  • (1)如果使用了 @MappedTypes 注解,则解析注解中指定的 Java 类型,调用 #register(Class<?> javaTypeClass, Class<?> typeHandlerClass) 注册处理器,其源码如下:
  public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
    register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass));
  }
  • (1.1)先创建对应的处理器实例,再调用 #register(Type javaType, TypeHandler<? extends T> typeHandler) 处理,注册指定 JavaType 的指定 TypeHandler 对象。

  • (2)若未使用该注解,则调用 #register(TypeHandler<T> typeHandler) 方法 ② 直接注册。

② register(TypeHandler<T> typeHandler)

  public <T> void register(TypeHandler<T> typeHandler) {
    boolean mappedTypeFound = false;
    // (1)获得 @MappedTypes 注解
    MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);
    // (2)优先使用 @MappedTypes 注解的 JavaType 进行注册
    if (mappedTypes != null) {
      for (Class<?> handledType : mappedTypes.value()) {
        register(handledType, typeHandler);
        mappedTypeFound = true;
      }
    }
    // (3)其次,当 typeHandler 为 TypeReference 子类时,解析其中的 JavaType 进行注册
    // @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
      }
    }
    // (4)最差,使用 JavaType 为 null 进行注册
    if (!mappedTypeFound) {
      register((Class<T>) null, typeHandler);
    }
  }

【解析】

  • (1)获得 @MappedTypes 注解。
  • (2)优先使用 @MappedTypes 注解的 JavaType 进行注册。
  • (3)其次,当 typeHandler 为 TypeReference 子类时,解析其中的 JavaType 进行注册。
  • (4)最差,使用 JavaType 为 null 进行注册。

register(Class<T> javaType, TypeHandler<? extends T> typeHandler)
③ register(Type javaType, TypeHandler<? extends T> typeHandler)

  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) {
    // (1)从处理器定义中获取 @MappedJdbcTypes 的标注
    MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
    if (mappedJdbcTypes != null) {
      // (1.1)解析注解中定义的 JdbcType 并注册
      for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
        register(javaType, handledJdbcType, typeHandler);
      }
      // (1.2)注解中还声明要注册一个 null 的 JdbcType
      if (mappedJdbcTypes.includeNullJdbcType()) {
        register(javaType, null, typeHandler);
      }
    } else {
      // (2)没有解析到 JdbcType,直接注册一个 null 的 JdbcType
      register(javaType, null, typeHandler);
    }
  }

【解析】
  方法① 中的两个分支,最后都会调用 #register(Class<T>, TypeHandler),该方法中将 Java 类型的 Class 对象向上转型为 Type 对象,再转给 #register(Type, TypeHandler) 方法处理,在该方法中:

  • (1)优先使用 @MappedJdbcTypes
    • (1.1)解析注解中定义的 JdbcType 并注册。
    • (1.2)注解指定要注册一个 JdbcType 为空的注册信息。
  • (2)没有使用注解,JdbcType 默认为空。
  • (3)上述两个分支都是调用方法④ 。

  上述的方法除了方法④ 都是入参没明确指定 Java 类型和 Jdbc 类型,下面这两个方法,对应调用关系图的右侧分支,就直接指定了。

register(Class<?> javaTypeClass, JdbcType jdbcType, Class<?> typeHandlerClass)
register(Class<T> type, JdbcType jdbcType, TypeHandler<? extends T> handler)

【源码与注解】

  public void register(Class<?> javaTypeClass, JdbcType jdbcType, Class<?> typeHandlerClass) {
    // 调用 getInstance() 创建处理器对象
    register(javaTypeClass, jdbcType, getInstance(javaTypeClass, typeHandlerClass));
  }
  public <T> void register(Class<T> type, JdbcType jdbcType, TypeHandler<? extends T> handler) {
    // 将 java Class 向上转型为 java Type
    register((Type) type, jdbcType, handler);
  }

【解析】
  将 java Class 向上转型为 java Type,再调用方法④ 。

  在右侧分支,还有两个方法。

register(String javaTypeClassName, String typeHandlerClassName)

  public void register(String javaTypeClassName, String typeHandlerClassName) throws ClassNotFoundException {
    register(Resources.classForName(javaTypeClassName), Resources.classForName(typeHandlerClassName));
  }

【解析】
  传入的是 Java 类型和处理器类型的类权限定名,调用 Resources.classForName 先加载对应的 Class 对象,再调用 #register(Class, Class) 方法。

register(TypeReference<T> javaTypeReference, TypeHandler<? extends T> handler)

  public <T> void register(TypeReference<T> javaTypeReference, TypeHandler<? extends T> handler) {
    register(javaTypeReference.getRawType(), handler);
  }

【解析】
  该方法主要是获取 TypeReference 中的泛型类型,然后调用 #register(Type, TypeHandler) 方法处理。

  最后在调用关系图中,还有一个独立的方法⑤。

⑤ register(JdbcType jdbcType, TypeHandler<?> handler)

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

【解析】很简单,直接注册到 JDBC_TYPE_HANDLER_MAP 集合中。

最后总结一下:

(1)如果使用包扫描注册用户自定义处理器,需要配合使用注解 @MappedTypes@MappedJdbcTypes,分别指定要注册的 Java 类型和 Jdbc 类型。

  • 若只用了 @MappedTypes,则注册进去的 Java 类型为 null
  • 若只用了 @MappedJdbcTypes,则注册进去的 Jdbc 类型为 null

整个调用关系图的左侧和中间的方法,都依赖于这两个注解,不过都不常用。

(2)如果指定了 Java 类型和 Jdbc 类型,则使用右侧的两个方法。

上述的(1)(2)最终都会填充 TYPE_HANDLER_MAPALL_TYPE_HANDLERS_MAP 集合,

(3)独立的方法⑤,专门用来注册填充 JDBC_TYPE_HANDLER_MAP 集合。

五、查找处理器

  TypeHandlerRegistry 另一类重要的方法就是查找处理器的 #getTypeHandler() 方法,同样该方法也提供了丰富的重载实现,其调用关系如下图所示:


  从调用关系图中可看出,方法 ① 是比较重要的核心方法,有三种方式查找处理器,都要调用到方法 ①,所以先介绍该方法的实现,源码如下:
  private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
    // 忽略 ParamMap 的情况
    if (ParamMap.class.equals(type)) {
        return null;
    }

    // (1) 查找 java 类型对应的 jdbc 类型和处理器的集合
    Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
    TypeHandler<?> handler = null;
    if (jdbcHandlerMap != null) {
      // (2.1) 优先根据 jdbcType 获取对应的 handler
      handler = jdbcHandlerMap.get(jdbcType);
      if (handler == null) {
        // (2.2) 其次,使用 null 获取对应的 TypeHandler ,可以认为是默认的 TypeHandler
        handler = jdbcHandlerMap.get(null);
      }
      // (2.3) 最差,从 TypeHandler 集合中选择一个唯一的 TypeHandler
      if (handler == null) {
        // #591
        handler = pickSoleHandler(jdbcHandlerMap);
      }
    }
    // (3) 若经过上面处理还没找到对应的 handler,且 javaType 是一个枚举类型,则返回默认的 EnumTypeHandler
    if (handler == null && type != null && type instanceof Class && Enum.class.isAssignableFrom((Class<?>) type)) {
      handler = new EnumTypeHandler((Class<?>) type);
    }
    // type drives generics here
    return (TypeHandler<T>) handler;
  }

【解析】

  • (1)查找 java 类型对应的 jdbc 类型和处理器映射Map。
  • (2)若该 Map 不为空
    • (2.1)优先根据 jdbcType 获取对应的 handler。
    • (2.2)其次,使用 null 获取对应的 TypeHandler ,可以认为是默认的 TypeHandler。
    • (2.3)最差,从 TypeHandler 集合中选择一个唯一的 TypeHandler,调用 #pickSoleHandler() 方法实现,获得该 Map 中唯一的 TypeHandler,该方法源码如下:
  private TypeHandler<?> pickSoleHandler(Map<JdbcType, TypeHandler<?>> jdbcHandlerMap) {
    TypeHandler<?> soleHandler = null;
    // 执行到这里 jdbcHandlerMap 肯定不为空,若该Map中若存在多个映射,则返回空,否则返回唯一的 handler
    for (TypeHandler<?> handler : jdbcHandlerMap.values()) {
      if (soleHandler == null) {
        soleHandler = handler;
      } else if (!handler.getClass().equals(soleHandler.getClass())) {
        // More than one type handlers registered.
        return null;
      }
    }
    return soleHandler;
  }
  • (3)若 java 类型是枚举类型,且经过上述步骤没找到 handler,则返回处理枚举类型默认的 EnumTypeHandler

调用关系图中的第一种情况

TypeHandler<T> getTypeHandler(Class<T> type)

  public <T> TypeHandler<T> getTypeHandler(Class<T> type) {
    // 指定 jdbcType 为 null,调用方法 ①
    return getTypeHandler((Type) type, null);
  }

调用关系图中的第二种情况

TypeHandler<T> getTypeHandler(Class<T> type, JdbcType jdbcType)

  public <T> TypeHandler<T> getTypeHandler(Class<T> type, JdbcType jdbcType) {
    // 将 java Class 转成 java type
    return getTypeHandler((Type) type, jdbcType);
  }

调用关系图中的第三种情况

  public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference) {
    return getTypeHandler(javaTypeReference, null);
  }
  public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference, JdbcType jdbcType) {
    // 获取 TypeReference 中的泛型类型作为 java 类型
    return getTypeHandler(javaTypeReference.getRawType(), jdbcType);
  }

六、其他方法

  比较简单,都是根据参数判断注册表中是否有处理器。

  public boolean hasTypeHandler(Class<?> javaType) {
    return hasTypeHandler(javaType, null);
  }

  public boolean hasTypeHandler(TypeReference<?> javaTypeReference) {
    return hasTypeHandler(javaTypeReference, null);
  }

  public boolean hasTypeHandler(Class<?> javaType, JdbcType jdbcType) {
    return javaType != null && getTypeHandler((Type) javaType, jdbcType) != null;
  }

  public boolean hasTypeHandler(TypeReference<?> javaTypeReference, JdbcType jdbcType) {
    return javaTypeReference != null && getTypeHandler(javaTypeReference, jdbcType) != null;
  }

  public TypeHandler<?> getMappingTypeHandler(Class<? extends TypeHandler<?>> handlerType) {
    return ALL_TYPE_HANDLERS_MAP.get(handlerType);
  }

  public TypeHandler<Object> getUnknownTypeHandler() {
    return UNKNOWN_TYPE_HANDLER;
  }

七、内置初始化注册表

  MyBatis 在初始化时会调用 TypeHandlerRegistry 的构造方法创建一个类型转换处理器注册表,该构造方法如下:

  public TypeHandlerRegistry() {
    // 不指定 jdbcType,会注册一个 null
    // <JavaType, <JdbcType, TypeHandler>>
    // <Boolean, <null, BooleanTypeHandler>
    // 这样在做参数绑定时,若 java 类型为 Boolean,则只会调用唯一的 BooleanTypeHandler 处理
    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    // 填充 JDBC_TYPE_HANDLER_MAP
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());

    register(Byte.class, new ByteTypeHandler());
    register(byte.class, new ByteTypeHandler());
    register(JdbcType.TINYINT, new ByteTypeHandler());

    register(Short.class, new ShortTypeHandler());
    register(short.class, new ShortTypeHandler());
    register(JdbcType.SMALLINT, new ShortTypeHandler());

    register(Integer.class, new IntegerTypeHandler());
    register(int.class, new IntegerTypeHandler());
    register(JdbcType.INTEGER, new IntegerTypeHandler());

    register(Long.class, new LongTypeHandler());
    register(long.class, new LongTypeHandler());

    register(Float.class, new FloatTypeHandler());
    register(float.class, new FloatTypeHandler());
    register(JdbcType.FLOAT, new FloatTypeHandler());

    register(Double.class, new DoubleTypeHandler());
    register(double.class, new DoubleTypeHandler());
    register(JdbcType.DOUBLE, new DoubleTypeHandler());

    register(Reader.class, new ClobReaderTypeHandler());
    register(String.class, new StringTypeHandler());
    register(String.class, JdbcType.CHAR, new StringTypeHandler());
    register(String.class, JdbcType.CLOB, new ClobTypeHandler());
    register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
    register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
    register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
    register(JdbcType.CHAR, new StringTypeHandler());
    register(JdbcType.VARCHAR, new StringTypeHandler());
    register(JdbcType.CLOB, new ClobTypeHandler());
    register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
    register(JdbcType.NVARCHAR, new NStringTypeHandler());
    register(JdbcType.NCHAR, new NStringTypeHandler());
    register(JdbcType.NCLOB, new NClobTypeHandler());

    register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
    register(JdbcType.ARRAY, new ArrayTypeHandler());

    register(BigInteger.class, new BigIntegerTypeHandler());
    register(JdbcType.BIGINT, new LongTypeHandler());

    register(BigDecimal.class, new BigDecimalTypeHandler());
    register(JdbcType.REAL, new BigDecimalTypeHandler());
    register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
    register(JdbcType.NUMERIC, new BigDecimalTypeHandler());

    register(InputStream.class, new BlobInputStreamTypeHandler());
    register(Byte[].class, new ByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
    register(byte[].class, new ByteArrayTypeHandler());
    register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
    register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.BLOB, new BlobTypeHandler());

    register(Object.class, UNKNOWN_TYPE_HANDLER);
    register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
    register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);

    register(Date.class, new DateTypeHandler());
    register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
    register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
    register(JdbcType.TIMESTAMP, new DateTypeHandler());
    register(JdbcType.DATE, new DateOnlyTypeHandler());
    register(JdbcType.TIME, new TimeOnlyTypeHandler());

    register(java.sql.Date.class, new SqlDateTypeHandler());
    register(java.sql.Time.class, new SqlTimeTypeHandler());
    register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());

    // jsr310类型处理标准,针对一些新的类型增添了一些对应的处理器,需要引入依赖
    // mybatis-typehandlers-jsr310
    try {
      // since 1.0.0
      register("java.time.Instant", "org.apache.ibatis.type.InstantTypeHandler");
      register("java.time.LocalDateTime", "org.apache.ibatis.type.LocalDateTimeTypeHandler");
      register("java.time.LocalDate", "org.apache.ibatis.type.LocalDateTypeHandler");
      register("java.time.LocalTime", "org.apache.ibatis.type.LocalTimeTypeHandler");
      register("java.time.OffsetDateTime", "org.apache.ibatis.type.OffsetDateTimeTypeHandler");
      register("java.time.OffsetTime", "org.apache.ibatis.type.OffsetTimeTypeHandler");
      register("java.time.ZonedDateTime", "org.apache.ibatis.type.ZonedDateTimeTypeHandler");
      // since 1.0.1
      register("java.time.Month", "org.apache.ibatis.type.MonthTypeHandler");
      register("java.time.Year", "org.apache.ibatis.type.YearTypeHandler");

    } catch (ClassNotFoundException e) {
      // no JSR-310 handlers
    }

    // issue #273
    register(Character.class, new CharacterTypeHandler());
    register(char.class, new CharacterTypeHandler());
  }

相关文章

网友评论

    本文标题:[MyBatis源码分析 - 类型模块 - 组件二] TypeH

    本文链接:https://www.haomeiwen.com/subject/bazevktx.html