在向数据库存储或读取数据时,我们需要将数据库字段类型和 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 个不同的类型处理器注册方法。这些注册方法的逻辑不难理解,但是
重载方法很多,上面调用的注册方法只是重载方法的一部分。由于重载太多且重载方法之间
互相调用,导致这一块的代码有点凌乱。
在上面的调用图中,每个蓝色背景框下都有一个标签。每个标签上面都已一个编号,这
些编号与上面代码中的标签是一致的。这里我把蓝色背景框内的方法称为开始方法,红色背
景框内的方法称为终点方法,白色背景框内的方法称为中间方法。
按照③→②→④→①的顺序进行分析
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); } } }
|