1. 概述
本文,我们来分享 MyBatis 的类型模块,对应 type
包。如下图所示:`type` 包
在 《精尽 MyBatis 源码解析 —— 项目结构一览》 中,简单介绍了这个模块如下:
① MyBatis 为简化配置文件提供了别名机制,该机制是类型转换模块的主要功能之一。
② 类型转换模块的另一个功能是实现 JDBC 类型与 Java 类型之间的转换,该功能在为 SQL 语句绑定实参以及映射查询结果集时都会涉及:
- 在为 SQL 语句绑定实参时,会将数据由 Java 类型转换成 JDBC 类型。
- 而在映射结果集时,会将数据由 JDBC 类型转换成 Java 类型。
2. TypeHandler
org.apache.ibatis.type.TypeHandler
,类型转换处理器。代码如下:
// TypeHandler.java |
- 一共有两类方法,分别是:
#setParameter(...)
方法,是Java Type => JDBC Type
的过程。#getResult(...)
方法,是JDBC Type => Java Type
的过程。
- 流程如下图:
流程
- 左边是
#setParameter(...)
方法,是Java Type => JDBC Type
的过程,从上往下看。 - 右边是
#getResult(...)
方法,是JDBC Type => Java Type
的过程,从下往上看。
- 左边是
2.1 BaseTypeHandler
org.apache.ibatis.type.BaseTypeHandler
,实现 TypeHandler 接口,继承 TypeReference 抽象类,TypeHandler 基础抽象类。
- 关于 TypeReference ,我们在 「3. TypeHandler」 中,详细解析。
2.1.1 setParameter
#setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)
方法,代码如下:
// BaseTypeHandler.java |
<1>
处,参数为空,设置为null
类型。<2>
处,参数非空,调用#setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)
抽象方法,设置对应的参数。代码如下:// BaseTypeHandler.java
public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;- 该方法由子类实现。
- 当发生异常时,统一抛出 TypeException 异常。
2.1.2 getResult
#getResult(...)
方法,代码如下:
// BaseTypeHandler.java |
调用
#getNullableResult(...)
抽象方法,获得指定结果的字段值。代码如下:// BaseTypeHandler.java
public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;
public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;- 该方法由子类实现。
当发生异常时,统一抛出 ResultMapException 异常。
2.2 子类
TypeHandler 有非常多的子类,当然所有子类都是继承自 BaseTypeHandler 抽象类。考虑到篇幅,我们就挑选几个来聊聊。
2.2.1 IntegerTypeHandler
org.apache.ibatis.type.IntegerTypeHandler
,继承 BaseTypeHandler 抽象类,Integer 类型的 TypeHandler 实现类。代码如下:
// IntegerTypeHandler.java |
- 比较简单,胖友瞅瞅。比较有意思的是
ResultSet#wasNull()
方法,它会判断最后读取的字段是否为空。
2.2.2 DateTypeHandler
org.apache.ibatis.type.DateTypeHandler
,继承 BaseTypeHandler 抽象类,Date 类型的 TypeHandler 实现类。代码如下:
// DateTypeHandler.java |
java.util.Date
和java.sql.Timestamp
的互相转换。
2.2.3 DateOnlyTypeHandler
org.apache.ibatis.type.DateOnlyTypeHandler
,继承 BaseTypeHandler 抽象类,Date 类型的 TypeHandler 实现类。代码如下:
// DateOnlyTypeHandler.java |
java.util.Date
和java.sql.Date
的互相转换。- 数据库里的时间有多种类型,以 MySQL 举例子,有
date
、timestamp
、datetime
三种类型。
2.2.4 EnumTypeHandler
org.apache.ibatis.type.EnumTypeHandler
,继承 BaseTypeHandler 抽象类,Enum 类型的 TypeHandler 实现类。代码如下:
// EnumTypeHandler.java |
java.lang.Enum
和java.util.String
的互相转换。- 因为数据库不存在枚举类型,所以讲枚举类型持久化到数据库有两种方式,
Enum.name <=> String
和Enum.ordinal <=> int
。我们目前看到的 EnumTypeHandler 是前者,下面我们将看到的 EnumOrdinalTypeHandler 是后者。
2.2.5 EnumOrdinalTypeHandler
org.apache.ibatis.type.EnumOrdinalTypeHandler
,继承 BaseTypeHandler 抽象类,Enum 类型的 TypeHandler 实现类。代码如下:
// EnumOrdinalTypeHandler.java |
java.lang.Enum
和int
的互相转换。
2.2.6 ObjectTypeHandler
org.apache.ibatis.type.ObjectTypeHandler
,继承 BaseTypeHandler 抽象类,Object 类型的 TypeHandler 实现类。代码如下:
// ObjectTypeHandler.java |
2.2.7 UnknownTypeHandler
org.apache.ibatis.type.UnknownTypeHandler
,继承 BaseTypeHandler 抽象类,未知的 TypeHandler 实现类。通过获取对应的 TypeHandler ,进行处理。代码如下:
// UnknownTypeHandler.java |
- 代码比较简单,胖友自己瞅瞅。
3. TypeReference
org.apache.ibatis.type.TypeReference
,引用泛型抽象类。目的很简单,就是解析类上定义的泛型。代码如下:
// TypeReference.java |
- 举个例子,「2.2.1 IntegerTypeHandler」 解析后的结果
rawType
为 Integer 。 【1】
处,从父类中获取<T>
。举个例子,代码如下:// GenericTypeSupportedInHierarchiesTestCase.java 的内部静态类
public static final class CustomStringTypeHandler extends StringTypeHandler {
/**
* Defined as reported in #581
*/
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
// do something
super.setNonNullParameter(ps, i, parameter, jdbcType);
}
}- 因为 CustomStringTypeHandler 自身是没有泛型的,需要从父类 StringTypeHandler 中获取。并且,获取的结果会是
rawType
为 String 。
- 因为 CustomStringTypeHandler 自身是没有泛型的,需要从父类 StringTypeHandler 中获取。并且,获取的结果会是
【2】
处,从当前类获取<T>
。
4. 注解
type
包中,也定义了三个注解,我们逐个来看看。
4.1 @MappedTypes
org.apache.ibatis.type.@MappedTypes
,匹配的 Java Type 类型的注解。代码如下:
// MappedTypes.java |
4.2 @MappedJdbcTypes
org.apache.ibatis.type.@MappedJdbcTypes
,匹配的 JDBC Type 类型的注解。代码如下:
// MappedJdbcTypes.java |
4.3 Alias
org.apache.ibatis.type.@Alias
,别名的注解。代码如下:
// Alias.java |
5. JdbcType
org.apache.ibatis.type.JdbcType
,Jdbc Type 枚举。代码如下:
// JdbcType.java |
6. TypeHandlerRegistry
org.apache.ibatis.type.TypeHandlerRegistry
,TypeHandler 注册表,相当于管理 TypeHandler 的容器,从其中能获取到对应的 TypeHandler 。
6.1 构造方法
// TypeHandlerRegistry.java |
TYPE_HANDLER_MAP
属性,TypeHandler 的映射。- 一个 Java Type 可以对应多个 JDBC Type ,也就是多个 TypeHandler ,所以 Map 的第一层的值是
Map<JdbcType, TypeHandler<?>
。在<1>
处,我们可以看到,Date 对应了多个 JDBC 的 TypeHandler 的注册。 - 当一个 Java Type 不存在对应的 JDBC Type 时,就使用
NULL_TYPE_HANDLER_MAP
静态属性,添加到TYPE_HANDLER_MAP
中进行占位。
- 一个 Java Type 可以对应多个 JDBC Type ,也就是多个 TypeHandler ,所以 Map 的第一层的值是
JDBC_TYPE_HANDLER_MAP
属性,JDBC Type 和 TypeHandler 的映射。- 一个 JDBC Type 只对应一个 Java Type ,也就是一个 TypeHandler ,不同于
TYPE_HANDLER_MAP
属性。在<2>
处,我们可以看到,我们可以看到,三个时间类型的 JdbcType 注册到JDBC_TYPE_HANDLER_MAP
中。 - 那么可能会有胖友问,
JDBC_TYPE_HANDLER_MAP
是一一映射,简单就可以获得 JDBC Type 对应的 TypeHandler ,而TYPE_HANDLER_MAP
是一对多映射,一个 JavaType 怎么获取到对应的 TypeHandler 呢?继续往下看,答案在#getTypeHandler(Type type, JdbcType jdbcType)
方法。
- 一个 JDBC Type 只对应一个 Java Type ,也就是一个 TypeHandler ,不同于
ALL_TYPE_HANDLERS_MAP
属性,所有 TypeHandler 的“集合” 。UNKNOWN_TYPE_HANDLER
属性,UnknownTypeHandler 对象,用于 Object 类型的注册。defaultEnumTypeHandler
属性,默认的枚举类型的 TypeHandler 对象。- 在构造方法中,有默认的 Java Type 和 JDBC Type 对 TypeHandler 的注册,因为有丢丢多,所以被艿艿省略了。
- 另外,这里的变量命名是不符合 Java 命名规范,不要学习。
6.2 getInstance
#getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass)
方法,创建 TypeHandler 对象。代码如下:
// TypeHandlerRegistry.java |
<1>
处,获得 Class 类型的构造方法,适合 「2.2.4 EnumTypeHandler」 的情况。<2>
处,获得空参的构造方法,适合 「2.2.1 IntegerTypeHandler」 的情况。
6.3 register
#register(...)
方法,注册 TypeHandler 。TypeHandlerRegistry 中有大量该方法的重载实现,大体整理如下:
FROM 徐郡明 《MyBatis 技术内幕》
除了 ⑤ 以外,所有方法最终都会调用 ④ ,即 #register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler)
方法,代码如下:
// TypeHandlerRegistry.java |
<1>
处,添加handler
到TYPE_HANDLER_MAP
中。<2>
处,添加handler
到ALL_TYPE_HANDLERS_MAP
中。- 这个方法还是比较简单的,我们看看其他调用的方法。
① #register(String packageName)
方法,扫描指定包下的所有 TypeHandler 类,并发起注册。代码如下:
// TypeHandlerRegistry.java |
在方法,会调用 ⑥
#register(Class<?> typeHandlerClass)
方法,注册指定 TypeHandler 类。代码如下:// TypeHandlerRegistry.java
public void register(Class<?> typeHandlerClass) {
boolean mappedTypeFound = false;
// <3> 获得 @MappedTypes 注解
MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
if (mappedTypes != null) {
// 遍历注解的 Java Type 数组,逐个进行注册
for (Class<?> javaTypeClass : mappedTypes.value()) {
register(javaTypeClass, typeHandlerClass);
mappedTypeFound = true;
}
}
// <4> 未使用 @MappedTypes 注解,则直接注册
if (!mappedTypeFound) {
register(getInstance(null, typeHandlerClass)); // 创建 TypeHandler 对象
}
}- 分成
<3>
<4>
两种情况。 <3>
处,基于@MappedTypes
注解,调用#register(Class<?> javaTypeClass, Class<?> typeHandlerClass)
方法,注册指定 Java Type 的指定 TypeHandler 类。代码如下:// TypeHandlerRegistry.java
public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass)); // 创建 TypeHandler 对象
}调用 ③
#register(Class<T> javaType, TypeHandler<? extends T> typeHandler)
方法,注册指定 Java Type 的指定 TypeHandler 对象。代码如下:// TypeHandlerRegistry.java
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
// 获得 MappedJdbcTypes 注解
MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
if (mappedJdbcTypes != null) {
// 遍历 MappedJdbcTypes 注册的 JDBC Type 进行注册
for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
register(javaType, handledJdbcType, typeHandler);
}
if (mappedJdbcTypes.includeNullJdbcType()) {
// <5>
register(javaType, null, typeHandler); // jdbcType = null
}
} else {
// <5>
register(javaType, null, typeHandler); // jdbcType = null
}
}- 有
@MappedJdbcTypes
注解的 ④#register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler)
方法,发起最终注册。 - 对于
<5>
处,发起注册时,jdbcType
参数为null
,这是为啥?😈 下文详细解析。
- 有
<4>
处,调用 ②#register(TypeHandler<T> typeHandler)
方法,未使用@MappedTypes
注解,调用#register(TypeHandler<T> typeHandler)
方法,注册 TypeHandler 对象。代码如下:// TypeHandlerRegistry.java
public <T> void register(TypeHandler<T> typeHandler) {
boolean mappedTypeFound = false;
// <5> 获得 @MappedTypes 注解
MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);
// 优先,使用 @MappedTypes 注解的 Java Type 进行注册
if (mappedTypes != null) {
for (Class<?> handledType : mappedTypes.value()) {
register(handledType, typeHandler);
mappedTypeFound = true;
}
}
// @since 3.1.0 - try to auto-discover the mapped type
// <6> 其次,当 typeHandler 为 TypeReference 子类时,进行注册
if (!mappedTypeFound && typeHandler instanceof TypeReference) {
try {
TypeReference<T> typeReference = (TypeReference<T>) typeHandler;
register(typeReference.getRawType(), typeHandler); // Java Type 为 <T> 泛型
mappedTypeFound = true;
} catch (Throwable t) {
// maybe users define the TypeReference with a different type and are not assignable, so just ignore it
}
}
// <7> 最差,使用 Java Type 为 null 进行注册
if (!mappedTypeFound) {
register((Class<T>) null, typeHandler);
}
}- 分成三种情况,最终都是调用
#register(Type javaType, TypeHandler<? extends T> typeHandler)
方法,进行注册,也就是跳到 ③ 。 <5>
处,优先,有符合的@MappedTypes
注解时,使用@MappedTypes
注解的 Java Type 进行注册。<6>
处,其次,当typeHandler
为 TypeReference 子类时,使用<T>
作为 Java Type 进行注册。<7>
处,最差,使用null
作为 Java Type 进行注册。但是,这种情况下,只会将typeHandler
添加到ALL_TYPE_HANDLERS_MAP
中。因为,实际上没有 Java Type 。
- 分成三种情况,最终都是调用
- 分成
因为重载的方法有点多,理解起来可能比较绕,胖友可能会比较闷逼。哈哈哈,实际在自己写的过程中,也有点懵逼。那怎么办?大体理解就好,另外 @MappedTypes
和 @MappedJdbcTypes
这两个注解基本不会用到,所以也可以先“忽略”。
另外,#register(...)
方法,还有其它重载方法,胖友可以自己翻下。
⑤ #register(JdbcType jdbcType, TypeHandler<?> handler)
方法,注册 handler
到 JDBC_TYPE_HANDLER_MAP
中。代码如下:
// TypeHandlerRegistry.java |
- 和上述的
#register(...)
方法是不同的。
6.4 getTypeHandler
#getTypeHandler(...)
方法,获得 TypeHandler 。TypeHandlerRegistry 有大量该方法的重载实现,大体整体如下:
FROM 徐郡明 《MyBatis 技术内幕》
从图中,我们可以看到,最终会调用 ① 处的 #getTypeHandler(Type type, JdbcType jdbcType)
方法。当然,我们先来看看三种调用的情况:
调用情况一:
#getTypeHandler(Class<T> type)
方法,代码如下:// TypeHandlerRegistry.java
public <T> TypeHandler<T> getTypeHandler(Class<T> type) {
return getTypeHandler((Type) type, null);
}jdbcType
为null
。
调用情况二:
#getTypeHandler(Class<T> type, JdbcType jdbcType)
方法,代码如下:// TypeHandlerRegistry.java
public <T> TypeHandler<T> getTypeHandler(Class<T> type, JdbcType jdbcType) {
return getTypeHandler((Type) type, jdbcType);
}- 将
type
转换成 Type 类型。
- 将
调用情况三:
#getTypeHandler(TypeReference<T> javaTypeReference, ...)
方法,代码如下:// TypeHandlerRegistry.java
public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference) {
return getTypeHandler(javaTypeReference, null);
}
public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference, JdbcType jdbcType) {
return getTypeHandler(javaTypeReference.getRawType(), jdbcType);
}- 使用
<T>
泛型作为type
。
- 使用
下面,正式来看看 ① 处的 #getTypeHandler(Type type, JdbcType jdbcType)
方法。代码如下:
// TypeHandlerRegistry.java |
<1>
处,调用#getJdbcHandlerMap(Type type)
方法,获得 Java Type 对应的 TypeHandler 集合。代码如下:// TypeHandlerRegistry.java
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMap(Type type) {
// <1.1> 获得 Java Type 对应的 TypeHandler 集合
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
// <1.2> 如果为 NULL_TYPE_HANDLER_MAP ,意味着为空,直接返回
if (NULL_TYPE_HANDLER_MAP.equals(jdbcHandlerMap)) {
return null;
}
// <1.3> 如果找不到
if (jdbcHandlerMap == null && type instanceof Class) {
Class<?> clazz = (Class<?>) type;
// 枚举类型
if (clazz.isEnum()) {
// 获得父类对应的 TypeHandler 集合
jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(clazz, clazz);
// 如果找不到
if (jdbcHandlerMap == null) {
// 注册 defaultEnumTypeHandler ,并使用它
register(clazz, getInstance(clazz, defaultEnumTypeHandler));
// 返回结果
return TYPE_HANDLER_MAP.get(clazz);
}
// 非枚举类型
} else {
// 获得父类对应的 TypeHandler 集合
jdbcHandlerMap = getJdbcHandlerMapForSuperclass(clazz);
}
}
// <1.4> 如果结果为空,设置为 NULL_TYPE_HANDLER_MAP ,提升查找速度,避免二次查找
TYPE_HANDLER_MAP.put(type, jdbcHandlerMap == null ? NULL_TYPE_HANDLER_MAP : jdbcHandlerMap);
// 返回结果
return jdbcHandlerMap;
}<1.1>
处,获得 Java Type 对应的 TypeHandler 集合。<1.2>
处,如果为NULL_TYPE_HANDLER_MAP
,意味着为空,直接返回。原因可见<1.4>
处。<1.3>
处,找不到,则根据type
是否为枚举类型,进行不同处理。- 【枚举】
先调用
#getJdbcHandlerMapForEnumInterfaces(Class<?> clazz, Class<?> enumClazz)
方法, 获得父类对应的 TypeHandler 集合。代码如下:// TypeHandlerRegistry.java
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForEnumInterfaces(Class<?> clazz, Class<?> enumClazz) {
// 遍历枚举类的所有接口
for (Class<?> iface : clazz.getInterfaces()) {
// 获得该接口对应的 jdbcHandlerMap 集合
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(iface);
// 为空,递归 getJdbcHandlerMapForEnumInterfaces 方法,继续从父类对应的 TypeHandler 集合
if (jdbcHandlerMap == null) {
jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(iface, enumClazz);
}
// 如果找到,则从 jdbcHandlerMap 初始化中 newMap 中,并进行返回
if (jdbcHandlerMap != null) {
// Found a type handler regsiterd to a super interface
HashMap<JdbcType, TypeHandler<?>> newMap = new HashMap<>();
for (Entry<JdbcType, TypeHandler<?>> entry : jdbcHandlerMap.entrySet()) {
// Create a type handler instance with enum type as a constructor arg
newMap.put(entry.getKey(), getInstance(enumClazz, entry.getValue().getClass()));
}
return newMap;
}
}
// 找不到,则返回 null
return null;
}- 代码比较简单,看下代码注释。
- 找不到,则注册
defaultEnumTypeHandler
,并使用它。 - 【非枚举】
调用
#getJdbcHandlerMapForSuperclass(Class<?> clazz)
方法,获得父类对应的 TypeHandler 集合。代码如下:// TypeHandlerRegistry.java
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForSuperclass(Class<?> clazz) {
// 获得父类
Class<?> superclass = clazz.getSuperclass();
// 不存在非 Object 的父类,返回 null
if (superclass == null || Object.class.equals(superclass)) {
return null;
}
// 获得父类对应的 TypeHandler 集合
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(superclass);
// 找到,则直接返回
if (jdbcHandlerMap != null) {
return jdbcHandlerMap;
// 找不到,则递归 getJdbcHandlerMapForSuperclass 方法,继续获得父类对应的 TypeHandler 集合
} else {
return getJdbcHandlerMapForSuperclass(superclass);
}
}- 代码比较简单,看下代码注释。
<1.4>
处,如果结果为空,设置为NULL_TYPE_HANDLER_MAP
,提升查找速度,避免二次查找。
<2.1>
处,优先,使用jdbcType
获取对应的 TypeHandler 。<2.2>
处,其次,使用null
获取对应的 TypeHandler ,可以认为是默认的 TypeHandler 。这里是解决一个 Java Type 可能对应多个 TypeHandler 的方式之一。<2.3>
处,最差,调用#pickSoleHandler(Map<JdbcType, TypeHandler<?>> jdbcHandlerMap)
方法,从 TypeHandler 集合中选择一个唯一的 TypeHandler 。代码如下:// TypeHandlerRegistry.java
private TypeHandler<?> pickSoleHandler(Map<JdbcType, TypeHandler<?>> jdbcHandlerMap) {
TypeHandler<?> soleHandler = null;
for (TypeHandler<?> handler : jdbcHandlerMap.values()) {
// 选择一个
if (soleHandler == null) {
soleHandler = handler;
// 如果还有,并且不同类,那么不好选择,所以返回 null
} else if (!handler.getClass().equals(soleHandler.getClass())) {
// More than one type handlers registered.
return null;
}
}
return soleHandler;
}- 这段代码看起来比较绕,其实目的很清晰,就是选择第一个,并且不能有其它的不同类的处理器。
- 这里是解决一个 Java Type 可能对应多个 TypeHandler 的方式之一。
- 通过
<2.1>
+<2.2>
+<2.3>
三处,解决 Java Type 对应的 TypeHandler 集合。
#getTypeHandler(JdbcType jdbcType)
方法,获得 jdbcType
对应的 TypeHandler 。代码如下:
// TypeHandlerRegistry.java |
7. TypeAliasRegistry
org.apache.ibatis.type.TypeAliasRegistry
,类型与别名的注册表。通过别名,我们在 Mapper XML 中的 resultType
和 parameterType
属性,直接使用,而不用写全类名。
7.1 构造方法
// TypeAliasRegistry.java |
TYPE_ALIASES
属性,类型与别名的映射。- 构造方法,初始化默认的类型与别名。
- 另外,在
org.apache.ibatis.session.Configuration
构造方法中,也有默认的注册类型与别名。
7.2 registerAlias
#registerAlias(Class<?> type)
方法,注册指定类。代码如下:
// TypeAliasRegistry.java |
- 别名的规则
<1>
,默认为,简单类名。<2>
,可通过@Alias
注解的别名。
<3>
,调用#registerAlias(String alias, Class<?> value)
方法,注册类型与别名的注册表。代码如下:// TypeAliasRegistry.java
public void registerAlias(String alias, Class<?> value) {
if (alias == null) {
throw new TypeException("The parameter alias cannot be null");
}
// issue #748
// <1> 转换成小写
String key = alias.toLowerCase(Locale.ENGLISH);
if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) { // <2> 冲突,抛出 TypeException 异常
throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
}
// <3>
TYPE_ALIASES.put(key, value);
}
* `<1>` 处,将别名转换成**小写**。这样的话,无论我们在 Mapper XML 中,写 `String` 还是 `string` 甚至是 `STRING` ,都是对应的 String 类型。
* `<2>` 处,如果已经注册,并且类型不一致,说明有冲突,抛出 TypeException 异常。
* `<3>` 处,添加到 `TYPE_ALIASES` 中。
* 另外,`#registerAlias(String alias, String value)` 方法,也会调用该方法。代码如下:
// TypeAliasRegistry.java
public void registerAlias(String alias, String value) {
try {
registerAlias(alias,
Resources.classForName(value) // 通过类名的字符串,获得对应的类。
);
} catch (ClassNotFoundException e) {
throw new TypeException("Error registering type alias " + alias + " for " + value + ". Cause: " + e, e);
}
}
7.3 registerAliases
#registerAliases(String packageName, ...)
方法,扫描指定包下的所有类,并进行注册。代码如下:
// TypeAliasRegistry.java |
<1>
处,将别名转换成小写。<2.1>
处,首先,从TYPE_ALIASES
中获取对应的类型。<2.2>
处,其次,直接获取对应的类。所以,这个方法,同时处理了别名与全类名两种情况。<2.3>
处,最差,找不到对应的类,发生异常,抛出 TypeException 异常。
8. SimpleTypeRegistry
org.apache.ibatis.type.SimpleTypeRegistry
,简单类型注册表。代码如下:
// SimpleTypeRegistry.java |
9. ByteArrayUtils
org.apache.ibatis.type.ByteArrayUtils
,Byte 数组的工具类。代码如下:
// ByteArrayUtils.java |
666. 彩蛋
type
模块的代码,还是相对多的,不过比较简单的。
参考和推荐如下文章: