一、概述
Mybatis类型转换模块,除了定义了一些列的类型处理器(类型转换器)外,还提供了一些其他类,比如:类型处理器注册器类、别名注解类等。主要包含了以下类:
- Alias 注解类
别名注解 - JdbcType 枚举类
JdbcType枚举类,对应数据库表中字段的类型 - MappedJdbcTypes 注解类
用于指明类型处理器可以处理的JdbcType中的类型集合 - MappedTypes 注解类
用于指明该TypeHandler实现类能够处理的Java 类型的集合 - SimpleTypeRegistry 简单类型注册器
用于判断哪些类是简单类型,和JAVA的基本数据类型不完全一样 - TypeAliasRegistry 类别名注册器
用于取代复杂的类型全限定名,mybatis中用于映射器配置文件中进行参数类型与返回结果类型的设置。默认完成了大量基础类型别名的注册。 - TypeException 异常类
- TypeHandlerRegistry 类型处理器注册器
主要完成类型处理器的注册功能,同时也能对类型处理器进行统筹管理,其内部定义了集合来进行类型处理器的存取,同时定义了存取方法。默认完成了大量常见类型处理器的注册。
二、类详解
1、类型别名
- 用法
类型别名是为Java类型设置一个简单的名字。它只和 XML配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如:
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
当这样配置时,Blog可以用在任何使用domain.blog.Blog的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。看下面的例子:
@Alias("author")
public class Author {
...
}
注:上面类型别名的用法实例,来源Mybatis官方文档。
- 别名注解 Alias
根据上面实例可以知道,注解@Alias主要用来表示该类对应的简写名称,需要配合Mybatis的配置文件来使用。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Alias {
String value();
}
TypeAliasRegistry.registerAlias()方法用来处理别名注解,主要是解析注解,并把简写名和对应的java类型注册到类型别名注册器中。
public void registerAlias(Class<?> type) {
String alias = type.getSimpleName();
//判断需要注册别名的类上,是否有Alias注解,如果有就使用注解中的值,否则就用类的简名
Alias aliasAnnotation = type.getAnnotation(Alias.class);
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
//调用通用的别名注册方法实现类别名的注册
registerAlias(alias, type);
}
2、JdbcType 枚举类
JdbcType这个枚举类型表示了在JDBC中的数据类型,该枚举类中定义了TYPE_CODE宇段,表示JDBC类型在java.sql.Types中相应的常量编码,并通过一个静态集合codeLookup (Map<Integer,JdbcType>类型〉记录了常量编码与JdbcType 之间的对应关系。
这个枚举类的代码结构主要包括了
- 枚举值的定义,枚举值定义的时候,把对应的java.sql.Types常量编码,存储到了对应的TYPE_CODE字段中;
- 字段TYPE_CODE和codeLookup。其中,TYPE_CODE表示当前枚举值对应的在java.sql.Types中相应的常量编码;codeLookup记录了常量编码与JdbcType 之间的对应关系。
- 静态代码块 主要是把所有枚举值和java.sql.Types中相应的常量编码建立一一对应的关系,并存储到codeLookup对象中。
- 构造函数,提供定义枚举值的时候,可以和java.sql.Types中相应的常量编码建立对应关系。
- forCode()方法,提供根据java.sql.Types中相应的常量编码查询JdbcType的方法,供其他地方引用。
public enum JdbcType {
ARRAY(Types.ARRAY),
BIT(Types.BIT),
TINYINT(Types.TINYINT),
SMALLINT(Types.SMALLINT),
INTEGER(Types.INTEGER),
BIGINT(Types.BIGINT),
FLOAT(Types.FLOAT),
REAL(Types.REAL),
DOUBLE(Types.DOUBLE),
NUMERIC(Types.NUMERIC),
DECIMAL(Types.DECIMAL),
CHAR(Types.CHAR),
VARCHAR(Types.VARCHAR),
LONGVARCHAR(Types.LONGVARCHAR),
DATE(Types.DATE),
TIME(Types.TIME),
TIMESTAMP(Types.TIMESTAMP),
BINARY(Types.BINARY),
VARBINARY(Types.VARBINARY),
LONGVARBINARY(Types.LONGVARBINARY),
NULL(Types.NULL),
OTHER(Types.OTHER),
BLOB(Types.BLOB),
CLOB(Types.CLOB),
BOOLEAN(Types.BOOLEAN),
CURSOR(-10), // Oracle
UNDEFINED(Integer.MIN_VALUE + 1000),
NVARCHAR(Types.NVARCHAR), // JDK6
NCHAR(Types.NCHAR), // JDK6
NCLOB(Types.NCLOB), // JDK6
STRUCT(Types.STRUCT),
JAVA_OBJECT(Types.JAVA_OBJECT),
DISTINCT(Types.DISTINCT),
REF(Types.REF),
DATALINK(Types.DATALINK),
ROWID(Types.ROWID), // JDK6
LONGNVARCHAR(Types.LONGNVARCHAR), // JDK6
SQLXML(Types.SQLXML), // JDK6
DATETIMEOFFSET(-155); // SQL Server 2008
public final int TYPE_CODE;
private static Map<Integer,JdbcType> codeLookup = new HashMap<Integer,JdbcType>();
static {
for (JdbcType type : JdbcType.values()) {
codeLookup.put(type.TYPE_CODE, type);
}
}
JdbcType(int code) {
this.TYPE_CODE = code;
}
public static JdbcType forCode(int code) {
return codeLookup.get(code);
}
}
3、@MappedTypes、@MappedJdbcTypes
@MappedTypes注解用于指明该TypeHandler实现类能够处理的Java 类型的集合,@MappedJdbcTypes 注解用于指明该TypeHandler 实现类能够处理的JDBC类型集合。
- @MappedTypes注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MappedTypes {
Class<?>[] value();
}
- @MappedTypes注解解析器,用于注册相关类型处理器。代码如下:
public void register(Class<?> typeHandlerClass) {
boolean mappedTypeFound = false;
MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
if (mappedTypes != null) {
for (Class<?> javaTypeClass : mappedTypes.value()) {
register(javaTypeClass, typeHandlerClass);
mappedTypeFound = true;
}
}
if (!mappedTypeFound) {
register(getInstance(null, typeHandlerClass));
}
}
Mybatis中不止这一处解析该注解,仅用作分析说明
- @MappedJdbcTypes
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MappedJdbcTypes {
JdbcType[] value();
boolean includeNullJdbcType() default false;
}
- @MappedJdbcTypes注解解析器,用于注册相关类型处理器。代码如下:
/**
* 将类型处理器注册到对应的Java类型
* 解析类型处理器是否包含MappedJdbcTypes注解,如果有,把注解对应的所有jdbcType注册到对应的Map中,
* 否则,默认jdbcType=null,然后注册到对应的Map中。
* @param javaType
* @param typeHandler
*/
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
//获取类型处理器上的注解MappedJdbcTypes的值
MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
if (mappedJdbcTypes != null) {//注解不为空,说明类型处理器上有该注解
//MappedJdbcTypes注解的value是数组,循环把该注解中包含的jdbcType值注册到对应的Map对象中
for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
register(javaType, handledJdbcType, typeHandler);
}
if (mappedJdbcTypes.includeNullJdbcType()) {//如果有注解,且注解包含jdbcType=null的情况,将该类型注册到对应的Map对象中
register(javaType, null, typeHandler);
}
} else {//如果注解为空,说明没有注解,即默认jdbcType=null,并注册到对应的Map对象中
register(javaType, null, typeHandler);
}
}
4、注册器 SimpleTypeRegistry
简单类型注册器,主要用于判断哪些类是简单类型,和JAVA的基本数据类型不完全一样。这里简单类型主要包括了String、Byte、Short、Character、Integer、Long、Float、Double、Boolean、Date、Class、BigInteger、BigDecimal。提供了一个isSimpleType()方法,用来判断类是否属于简单类型。
public class SimpleTypeRegistry {
private static final Set<Class<?>> SIMPLE_TYPE_SET = new HashSet<Class<?>>();
static {
SIMPLE_TYPE_SET.add(String.class);
SIMPLE_TYPE_SET.add(Byte.class);
SIMPLE_TYPE_SET.add(Short.class);
SIMPLE_TYPE_SET.add(Character.class);
SIMPLE_TYPE_SET.add(Integer.class);
SIMPLE_TYPE_SET.add(Long.class);
SIMPLE_TYPE_SET.add(Float.class);
SIMPLE_TYPE_SET.add(Double.class);
SIMPLE_TYPE_SET.add(Boolean.class);
SIMPLE_TYPE_SET.add(Date.class);
SIMPLE_TYPE_SET.add(Class.class);
SIMPLE_TYPE_SET.add(BigInteger.class);
SIMPLE_TYPE_SET.add(BigDecimal.class);
}
private SimpleTypeRegistry() {
// Prevent Instantiation
}
public static boolean isSimpleType(Class<?> clazz) {
return SIMPLE_TYPE_SET.contains(clazz);
}
}
5、注册器 TypeAliasRegistry
类别名注册器。用于取代复杂的类型全限定名,mybatis中用于映射器配置文件中进行参数类型与返回结果类型的设置。默认完成了大量基础类型别名的注册。
- 字段
类别名注册器TypeAliasRegistry中,只有一个字段TYPE_ALIASES,该字段用来存储的别名和实际类型映射关系的地方,通过HashMap键值对实现。
private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();
- 构造函数
构造函数完成了大量基础类型别名的注册。需要注意的是,除了这里进行了别名的注册,在创建Configuration实例的时候在其无参构造器中也注册了一些类别名。
public TypeAliasRegistry() {
//字符串类型
registerAlias("string", String.class);
//基本包装类型
registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
//略
}
Configuration无参构造函数
public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
//略
}
- registerAlias(String alias, Class<?> value)方法
该方法,主要实现建立别名与对应类型的一一对应关系,本质上就是一个Map对象赋值的操作。其中,添加了别名为null、或者重复添加的判断逻辑。
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);
//如果已经存在key了,且value和之前不一致,报错
if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
}
//把该别名和类型注册到HashMap对象中
TYPE_ALIASES.put(key, value);
}
- registerAliases(String packageName)、registerAliases(String packageName, Class<?> superType)类
该方法主要实现根据包名进行解析别名的方式。其中registerAliases(String packageName)是一个重载简化参数的方法,实际上是由registerAliases(String packageName, Class<?> superType)方法实现功能。其中通过ResolverUtil工具类,获取指定包下符合条件的所有类,然后循环处理进行注册,其中用到了registerAlias(Class<?> type)方法。
/**
* 包统一注册方式,对应的是如下的设置方式
* <typeAliases>
* <package name="com.xx.xx.xx"/>
* </typeAliases>
* @param packageName
*/
public void registerAliases(String packageName){
registerAliases(packageName, Object.class);
}
/**
* 包统一注册方式,实现检索需要注册别名的类,并调用registerAlias(Class<?> type)实现注册
* 表示注册指定包名下的所有类,它调用了第二个方法,其第二个参数目的是限定要注册的类的来源,只有继承自给定类型的类才能被注册,
* 这里赋值为Object.class表示其下的所有类均在别名注册的考虑范围。
* @param packageName
* @param superType
*/
public void registerAliases(String packageName, Class<?> superType){
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
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);
}
}
}
- registerAlias(Class<?> type)方法
根据对应的类,进行类别名的注册。其中,用到了@Alias注解。当该类上有注解时,就用注解中的值,没有的话,就用类的简写名称。
public void registerAlias(Class<?> type) {
String alias = type.getSimpleName();
//判断需要注册别名的类上,是否有Alias注解,如果有就使用注解中的值,否则就用类的简名
Alias aliasAnnotation = type.getAnnotation(Alias.class);
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
//调用通用的别名注册方法实现类别名的注册
registerAlias(alias, type);
}
- registerAlias(String alias, String value)方法
主要实现逐个注册,因为在配置中已经将目标与所要设置的别名名称都指定好了,因此只需要直接执行核心注册方法即可完成注册工作,这种方式适合少量的类型注册情况,但是一旦需要注册的类型较多,工作就会显得很是复杂繁琐。为了简化工作我们可以采用之前第一种方式,要采用这种方式就要在架构编码时有意的将需要进行类型别名注册的类放置到统一的包下。
/**
* 逐个注册方式,对应的是如下的设置方式
* <typeAliases>
* <typeAlias type="com.xxx.xx.Role" alias="role" />
* </typeAliases>
*因为在配置中已经将目标与所要设置的别名名称都指定好了,因此只需要直接执行核心注册方法即可完成注册工作,
*这种方式适合少量的类型注册情况,但是一旦需要注册的类型较多,工作就会显得很是复杂繁琐,
*为了简化工作我们可以采用之前第一种方式,要采用这种方式就要在架构编码时有意的将需要进行类型别名注册的类放置到统一的包下。
* @param alias
* @param value
*/
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);
}
}
-resolveAlias(String string)方法
前面分析的都是registerAlias()方法及其重载方法,主要实现别名的注册。该方法主要用来解析别名。通过别名获取集合中保存的别名对应的值,即类的类型。
@SuppressWarnings("unchecked")
// throws class cast exception as well if types cannot be assigned
public <T> Class<T> resolveAlias(String string) {
try {
if (string == null) {//别名如果为空,就直接返回null
return null;
}
// issue #748
String key = string.toLowerCase(Locale.ENGLISH);
Class<T> value;
if (TYPE_ALIASES.containsKey(key)) {//如果在别名中注册了,就直接获取别名对应的类类型
value = (Class<T>) TYPE_ALIASES.get(key);
} else {
找不到,再试着将String直接转成Class(这样怪不得我们也可以直接用java.lang.Integer的方式定义,也可以就int这么定义)
value = (Class<T>) Resources.classForName(string);
}
return value;
} catch (ClassNotFoundException e) {
throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e);
}
}
6、注册器 TypeHandlerRegistry
类型处理器注册器。主要完成类型处理器的注册功能,同时也能对类型处理器进行统筹管理,其内部定义了集合来进行类型处理器的存取,同时定义了存取方法。默认完成了大量常见类型处理器的注册。
- 字段
类型处理器TypeHandlerRegistry 中,主要包括了一下字段:
- JDBC_TYPE_HANDLER_MAP
这是一个枚举Map集合,其内部是以JdbcType枚举类中枚举值为键创建的一种集合,即它是以数据库类型为键来保存类型处理器,将类型处理器注册到对应的数据库类型上。 - TYPE_HANDLER_MAP、
这是一个嵌套Map集合,内层集合是以数据库类型为键保存处理器,外层集合为以Java类型来保存对应的数据库类型及其处理器,这个集合将三者联系起来,是真正进行三者对应关系匹配的集合。 - UNKNOWN_TYPE_HANDLER
这个是上一篇中分析到的类型处理器,在在实际使用的过程中,根据JavaType和JdbcType来确定的处理器类型。 - ALL_TYPE_HANDLERS_MAP
这个集合中保存着所有的类型处理器,是以类型处理器的类类型为键值保存的,它可以统筹所有的类型处理器(带有统计的效果) - NULL_TYPE_HANDLER_MAP
空的 以JdbcType枚举类中枚举值为键创建的一种集合.用于判断一个Map<JdbcType, TypeHandler<?>>实例是否为空。 - defaultEnumTypeHandler
默认的枚举类型处理器
private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new ConcurrentHashMap<Type, Map<JdbcType, TypeHandler<?>>>();
private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();
private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();
private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;
- 构造函数
构造函数,主要通过register()的几个重载方法,实现了对默认Java类型或JDBC类型的数据,进行类型处理器的注册功能。主要用到了三类方法,下面会分别分析。
- register(Class javaType, TypeHandler<? extends T> typeHandler)
- register(Class type, JdbcType jdbcType, TypeHandler<? extends T> handler).
- register(JdbcType jdbcType, TypeHandler<?> handler)
public TypeHandlerRegistry() {
//逻辑值、布尔值
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
//略
//字符串,以下是为同一个类型的多种变种注册到多个不同的handler
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()方法及其重载方法
register()方法及其重载方法中,除了几个核心的处理方法外,其他的都是调用底层方法而实现,下面主要分析了核心的几个方法:
- register(JdbcType jdbcType, TypeHandler<?> handler)
建立jdbcType与类型处理器的映射关系,即把该映射关系注册到JDBC_TYPE_HANDLER_MAP集合对象中。
public void register(JdbcType jdbcType, TypeHandler<?> handler) {
JDBC_TYPE_HANDLER_MAP.put(jdbcType, handler);
}
- register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler)
该方法首先把类型、jdbcTpe、类型处理器对应的关系注册到TYPE_HANDLER_MAP集合中,然后把类型与类型处理器对应关系注册到ALL_TYPE_HANDLERS_MAP集合中。
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
if (javaType != null) {//javaType不为空时,为空时不注册到TYPE_HANDLER_MAP集合
//根据javaType获取jdbcType与类型处理键值对的集合
Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
if (map == null || map == NULL_TYPE_HANDLER_MAP) {//如果不存在,即没有被注册
//新建jdbcType与类型处理器键值对对象,并把该对象注册到TYPE_HANDLER_MAP对象
map = new HashMap<JdbcType, TypeHandler<?>>();
TYPE_HANDLER_MAP.put(javaType, map);
}
//重置map中的jdbcType和类型处理器,当原来map为空时,也是在这里进行了赋值,如果map不为空,即覆盖原来的值
map.put(jdbcType, handler);
}
//把javaType与类型处理器的映射关系注册到ALL_TYPE_HANDLERS_MAP对象中
ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
}
- register(Type javaType, TypeHandler<? extends T> typeHandler)
建立类型处理器注册与Java类型的对应关系。解析类型处理器是否包含MappedJdbcTypes注解,如果有,把注解对应的所有jdbcType注册到对应的Map中,否则,默认jdbcType=null,然后注册到对应的Map中。底层通过register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler)方法实现了数据的初始化。
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
//获取类型处理器上的注解MappedJdbcTypes的值
MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
if (mappedJdbcTypes != null) {//注解不为空,说明类型处理器上有该注解
//MappedJdbcTypes注解的value是数组,循环把该注解中包含的jdbcType值注册到对应的Map对象中
for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
register(javaType, handledJdbcType, typeHandler);
}
if (mappedJdbcTypes.includeNullJdbcType()) {//如果有注解,且注解包含jdbcType=null的情况,将该类型注册到对应的Map对象中
register(javaType, null, typeHandler);
}
} else {//如果注解为空,说明没有注解,即默认jdbcType=null,并注册到对应的Map对象中
register(javaType, null, typeHandler);
}
}
- register(TypeHandler typeHandler)
根据注解MappedTypes建立类类型与类型处理器的对应关系。
@SuppressWarnings("unchecked")
public <T> void register(TypeHandler<T> typeHandler) {
boolean mappedTypeFound = false;
MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);
if (mappedTypes != null) {
for (Class<?> handledType : mappedTypes.value()) {
register(handledType, typeHandler);
mappedTypeFound = true;
}
}
// @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(String packageName)
底层还是通过register(Class<?> typeHandlerClass)方法实现。
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
if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
register(type);
}
}
}
- getTypeHandler(Type type, JdbcType jdbcType)方法
根据javaType和jdbcType获取类型处理器的底层方法
@SuppressWarnings("unchecked")
private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
if (ParamMap.class.equals(type)) {//等于ParamMap类型时,直接返回null.
return null;
}
//根据type查询jdbcHandlerMap
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
//存储类型处理器
TypeHandler<?> handler = null;
if (jdbcHandlerMap != null) {
//查询type和jdbcType对应的类型处理器
handler = jdbcHandlerMap.get(jdbcType);
if (handler == null) {//如果类型处理器为空,继续查询jdbcType=null对应的类型处理器
handler = jdbcHandlerMap.get(null);
}
if (handler == null) {
// #591
handler = pickSoleHandler(jdbcHandlerMap);
}
}
// type drives generics here
return (TypeHandler<T>) handler;
}
- getJdbcHandlerMap(Type type)
根据javaType查询jdbcType与类型处理器对应关系的键值对
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMap(Type type) {
//根据javaType从TYPE_HANDLER_MAP获取jdbcType与类型处理器对应关系的键值对
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
if (NULL_TYPE_HANDLER_MAP.equals(jdbcHandlerMap)) {//如果等于空集合,直接返回null
return null;
}
if (jdbcHandlerMap == null && type instanceof Class) {//key:ifnull,如果为空,继续查询该类型超类对应的jdbcHandlerMap
Class<?> clazz = (Class<?>) type;
if (clazz.isEnum()) {//处理枚举类型处理器
//获取枚举类型处理器
jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(clazz, clazz);
if (jdbcHandlerMap == null) {//如果枚举类型处理器为空,
//把默认的枚举类型处理器defaultEnumTypeHandler注册进去
register(clazz, getInstance(clazz, defaultEnumTypeHandler));
//返回上面注册的类型处理器
return TYPE_HANDLER_MAP.get(clazz);
}
} else {//根据超类获取对应的jdbcHandlerMap
jdbcHandlerMap = getJdbcHandlerMapForSuperclass(clazz);
}
}
//把查询结果更新到TYPE_HANDLER_MAP集合中(当执行了key:ifnull语句时,就需要把最新的结果更新到TYPE_HANDLER_MAP)
TYPE_HANDLER_MAP.put(type, jdbcHandlerMap == null ? NULL_TYPE_HANDLER_MAP : jdbcHandlerMap);
return jdbcHandlerMap;
}
- **getJdbcHandlerMapForEnumInterfaces(Class<?> clazz, Class<?> enumClazz) **
获取枚举类型的类型处理器
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForEnumInterfaces(Class<?> clazz, Class<?> enumClazz) {
for (Class<?> iface : clazz.getInterfaces()) {
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(iface);
if (jdbcHandlerMap == null) {
jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(iface, enumClazz);
}
if (jdbcHandlerMap != null) {
// Found a type handler regsiterd to a super interface
HashMap<JdbcType, TypeHandler<?>> newMap = new HashMap<JdbcType, TypeHandler<?>>();
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;
}
}
return null;
}
- getJdbcHandlerMapForSuperclass(Class<?> clazz)
根据超类获取jdbcHandlerMap.
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForSuperclass(Class<?> clazz) {
Class<?> superclass = clazz.getSuperclass();
if (superclass == null || Object.class.equals(superclass)) {//如果没有超类或者超类是Object
return null;
}
//获取超类对应的jdbcHandlerMap
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(superclass);
if (jdbcHandlerMap != null) {//如果获取到就直接返回
return jdbcHandlerMap;
} else {//如果没有获取到,就继续递归查询上级超类对应的jdbcHandlerMap
return getJdbcHandlerMapForSuperclass(superclass);
}
}
7、异常类TypeException
Mybatis类型转换模块的异常处理类,比较简单,不做分析。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/68907.html