在向数据库存储或读取数据时,我们需要将数据库字段类型和 Java 类型进行一个转换。
比如数据库中有 CHAR 和 VARCHAR 等类型,但 Java 中没有这些类型,不过 Java 有 String
类型。所以我们在从数据库中读取 CHAR 和 VARCHAR 类型的数据时,就可以把它们转成
String。

在 MyBatis 中,数据库类型和 Java 类型之间的转换任务是委托给类型处理器TypeHandler 去处理的。MyBatis 提供了一些常见类型的类型处理器,除此之外,我们还可以自定义类型处理器以非常见类型转换的需求。

实例

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 自动扫᧿ -->
<typeHandlers>
<package name="xyz.coolblog.handlers"/>
</typeHandlers>


<!-- 手动配置 -->
<typeHandlers>
<typeHandler jdbcType="TINYINT"
javaType="xyz.coolblog.constant.ArticleTypeEnum"
handler="xyz.coolblog.mybatis.ArticleTypeHandler"/>
</typeHandlers>

使用自动扫描的方式注册类型处理器时,应使用@MappedTypes @MappedJdbcTypes
注解配置 javaType 和 jdbcType。

分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
private void typeHandlerElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
// 从指定的包中注册 TypeHandler
if ("package".equals(child.getName())) {
String typeHandlerPackage = child.getStringAttribute("name");
// 注册方法 ①
typeHandlerRegistry.register(typeHandlerPackage);
} else {
// 从 typeHandler 节点中解析别名到类型的映射
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);
// 根据 javaTypeClass 和 jdbcType 值的情况进行不同的注册策略
if (javaTypeClass != null) {
if (jdbcType == null) {
// 注册方法 ②
typeHandlerRegistry.register(
javaTypeClass, typeHandlerClass);
} else {
// 注册方法 ③
typeHandlerRegistry.register(
javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
// 注册方法 ④
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}

上面的代码中调用了 4 个不同的类型处理器注册方法。这些注册方法的逻辑不难理解,但是
重载方法很多,上面调用的注册方法只是重载方法的一部分。由于重载太多且重载方法之间
互相调用,导致这一块的代码有点凌乱。
image.png

在上面的调用图中,每个蓝色背景框下都有一个标签。每个标签上面都已一个编号,这
些编号与上面代码中的标签是一致的。这里我把蓝色背景框内的方法称为开始方法,红色背
景框内的方法称为终点方法,白色背景框内的方法称为中间方法。

按照③→②→④→①的顺序进行分析

register(Class,JdbcType,Class)⽅法分析

当代码执行到此方法时,表示javaTypeClass!=null&&jdbcType!=null条件成立立,即使用者明确配置了 javaType 和 jdbcType 属性的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void register(Class<?> javaTypeClass, 
JdbcType jdbcType, Class<?> typeHandlerClass) {
// 调用终点方法
register(javaTypeClass, jdbcType,
getInstance(javaTypeClass, typeHandlerClass));
}

/** 类型处理器注册过程的终点 */
private void register(Type javaType,
JdbcType jdbcType, TypeHandler<?> handler) {
if (javaType != null) {
// JdbcType 到 TypeHandler 的映射
Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
if (map == null || map == NULL_TYPE_HANDLER_MAP) {
map = new HashMap<JdbcType, TypeHandler<?>>();
// 存储 javaType 到 Map<JdbcType, TypeHandler> 的映射
TYPE_HANDLER_MAP.put(javaType, map);
}
map.put(jdbcType, handler);
}

// 存储所有的 TypeHandler
ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
}

所谓的注册过程也就是把类型和处理器进行映射而已,没什么特别之处。

register(Class,Class)⽅法分析

当代码执行到此方法时,表示 javaTypeClass != null && jdbcType == null 条件成立,即使用者仅设置了 javaType 属性的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
// 调用中间方法 register(Type, TypeHandler)
register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass));
}

private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
// 获取 @MappedJdbcTypes 注解
MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
if (mappedJdbcTypes != null) {
// 遍历 @MappedJdbcTypes 注解中配置的值
for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
// 调用终点方法,参考上一小节的分析
register(javaType, handledJdbcType, typeHandler);
}
if (mappedJdbcTypes.includeNullJdbcType()) {
// 调用终点方法,jdbcType = null
register(javaType, null, typeHandler);
}
} else {
// 调用终点方法,jdbcType = null
register(javaType, null, typeHandler);
}
}

register(Class)⽅法分析

当代码执行到此方法时,表示 javaTypeClass==null条件成立,即使用者未配置 javaType 和 jdbcType 属性的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public void register(Class<?> typeHandlerClass) {
boolean mappedTypeFound = false;
// 获取 @MappedTypes 注解
MappedTypes mappedTypes = typeHandlerClass
.getAnnotation(MappedTypes.class);
if (mappedTypes != null) {
// 遍历 @MappedTypes 注解中配置的值
for (Class<?> javaTypeClass : mappedTypes.value()) {
// 调用注册方法 ②
register(javaTypeClass, typeHandlerClass);
mappedTypeFound = true;
}
}
if (!mappedTypeFound) {
// 调用中间方法 register(TypeHandler)
register(getInstance(null, typeHandlerClass));
}
}

public <T> void register(TypeHandler<T> typeHandler) {
boolean mappedTypeFound = false;
// 获取 @MappedTypes 注解
MappedTypes mappedTypes =
typeHandler.getClass().getAnnotation(MappedTypes.class);
if (mappedTypes != null) {
for (Class<?> handledType : mappedTypes.value()) {
// 调用中间方法 register(Type, TypeHandler)
register(handledType, typeHandler);
mappedTypeFound = true;
}
}
// 自动发现映射类型
if (!mappedTypeFound && typeHandler instanceof TypeReference) {
try {
TypeReference<T> typeReference = (TypeReference<T>) typeHandler;
// 获取参数模板中的参数类型,并调用中间方法 register(Type, TypeHandler)
register(typeReference.getRawType(), typeHandler);
mappedTypeFound = true;
} catch (Throwable t) {
}
}
if (!mappedTypeFound) {
// 调用中间方法 register(Class, TypeHandler)
register((Class<T>) null, typeHandler);
}
}

public <T> void register(
Class<T> javaType, TypeHandler<? extends T> typeHandler) {
// 调用中间方法 register(Type, TypeHandler)
register((Type) javaType, typeHandler);
}

不管是通过注解的方式,还是通过反射的方式,它们最终目的是为了解析出 javaType 的值。解析完成后,这些方法会调用中间方法register(Type,TypeHandler),这个方法负责解析jdbcType

register(String)⽅法分析

代码的主要是用于自动扫描类型处理器,并调用其他方法注册扫描结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void register(String packageName) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
// 从指定包中查找 TypeHandler
resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
// 所有符合条件的classes
Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
// 遍历注册
for (Class<?> type : handlerSet) {
// 忽略内部类,接口,抽象类等
if (!type.isAnonymousClass() && !type.isInterface()
&& !Modifier.isAbstract(type.getModifiers())) {
// 调用注册方法 ④
register(type);
}
}
}