Mybatis类型转换模块,除了定义了一些列的类型处理器(类型转换器)外,还提供了一些其他类,比如:类型处理器注册器类、别名注解类等。主要包含了以下类:
当这样配置时,Blog可以用在任何使用domain.blog.Blog的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。看下面的例子:
@Alias("author")
public class Author {
...
}
注:上面类型别名的用法实例,来源Mybatis官方文档。
根据上面实例可以知道,注解@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);
}
JdbcType这个枚举类型表示了在JDBC中的数据类型,该枚举类中定义了TYPE_CODE宇段,表示JDBC类型在java.sql.Types中相应的常量编码,并通过一个静态集合codeLookup (Map
这个枚举类的代码结构主要包括了
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 codeLookup = new HashMap();
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);
}
}
@MappedTypes注解用于指明该TypeHandler实现类能够处理的Java 类型的集合,@MappedJdbcTypes 注解用于指明该TypeHandler 实现类能够处理的JDBC类型集合。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MappedTypes {
Class>[] value();
}
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中不止这一处解析该注解,仅用作分析说明
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MappedJdbcTypes {
JdbcType[] value();
boolean includeNullJdbcType() default false;
}
/**
* 将类型处理器注册到对应的Java类型
* 解析类型处理器是否包含MappedJdbcTypes注解,如果有,把注解对应的所有jdbcType注册到对应的Map中,
* 否则,默认jdbcType=null,然后注册到对应的Map中。
* @param javaType
* @param typeHandler
*/
private 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);
}
}
简单类型注册器,主要用于判断哪些类是简单类型,和JAVA的基本数据类型不完全一样。这里简单类型主要包括了String、Byte、Short、Character、Integer、Long、Float、Double、Boolean、Date、Class、BigInteger、BigDecimal。提供了一个isSimpleType()方法,用来判断类是否属于简单类型。
public class SimpleTypeRegistry {
private static final Set> SIMPLE_TYPE_SET = new HashSet>();
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);
}
}
类别名注册器。用于取代复杂的类型全限定名,mybatis中用于映射器配置文件中进行参数类型与返回结果类型的设置。默认完成了大量基础类型别名的注册。
private final Map> TYPE_ALIASES = new HashMap>();
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);
//略
}
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);
}
/**
* 包统一注册方式,对应的是如下的设置方式
*
*
*
* @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> resolverUtil = new ResolverUtil>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set>> 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);
}
}
}
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);
}
/**
* 逐个注册方式,对应的是如下的设置方式
*
*
*
*因为在配置中已经将目标与所要设置的别名名称都指定好了,因此只需要直接执行核心注册方法即可完成注册工作,
*这种方式适合少量的类型注册情况,但是一旦需要注册的类型较多,工作就会显得很是复杂繁琐,
*为了简化工作我们可以采用之前第一种方式,要采用这种方式就要在架构编码时有意的将需要进行类型别名注册的类放置到统一的包下。
* @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 Class resolveAlias(String string) {
try {
if (string == null) {//别名如果为空,就直接返回null
return null;
}
// issue #748
String key = string.toLowerCase(Locale.ENGLISH);
Class value;
if (TYPE_ALIASES.containsKey(key)) {//如果在别名中注册了,就直接获取别名对应的类类型
value = (Class) TYPE_ALIASES.get(key);
} else {
////找不到,再试着将String直接转成Class(这样怪不得我们也可以直接用java.lang.Integer的方式定义,也可以就int这么定义)
value = (Class) Resources.classForName(string);
}
return value;
} catch (ClassNotFoundException e) {
throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e);
}
}
类型处理器注册器。主要完成类型处理器的注册功能,同时也能对类型处理器进行统筹管理,其内部定义了集合来进行类型处理器的存取,同时定义了存取方法。默认完成了大量常见类型处理器的注册。
类型处理器TypeHandlerRegistry 中,主要包括了一下字段:
private final Map> JDBC_TYPE_HANDLER_MAP = new EnumMap>(JdbcType.class);
private final Map>> TYPE_HANDLER_MAP = new ConcurrentHashMap>>();
private final TypeHandler
构造函数,主要通过register()的几个重载方法,实现了对默认Java类型或JDBC类型的数据,进行类型处理器的注册功能。主要用到了三类方法,下面会分别分析。
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());
//略
}
public void register(JdbcType jdbcType, TypeHandler> handler) {
JDBC_TYPE_HANDLER_MAP.put(jdbcType, handler);
}
private void register(Type javaType, JdbcType jdbcType, TypeHandler> handler) {
if (javaType != null) {//javaType不为空时,为空时不注册到TYPE_HANDLER_MAP集合
//根据javaType获取jdbcType与类型处理键值对的集合
Map> map = TYPE_HANDLER_MAP.get(javaType);
if (map == null || map == NULL_TYPE_HANDLER_MAP) {//如果不存在,即没有被注册
//新建jdbcType与类型处理器键值对对象,并把该对象注册到TYPE_HANDLER_MAP对象
map = new HashMap>();
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);
}
private 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);
}
}
@SuppressWarnings("unchecked")
public void register(TypeHandler 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 typeReference = (TypeReference) 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) null, typeHandler);
}
}
public void register(String packageName) {
ResolverUtil> resolverUtil = new ResolverUtil>();
resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
Set>> 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);
}
}
}
@SuppressWarnings("unchecked")
private TypeHandler getTypeHandler(Type type, JdbcType jdbcType) {
if (ParamMap.class.equals(type)) {//等于ParamMap类型时,直接返回null.
return null;
}
//根据type查询jdbcHandlerMap
Map> 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) handler;
}
private Map> getJdbcHandlerMap(Type type) {
//根据javaType从TYPE_HANDLER_MAP获取jdbcType与类型处理器对应关系的键值对
Map> 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;
}
private Map> getJdbcHandlerMapForEnumInterfaces(Class> clazz, Class> enumClazz) {
for (Class> iface : clazz.getInterfaces()) {
Map> 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> newMap = new HashMap>();
for (Entry> 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;
}
private Map> getJdbcHandlerMapForSuperclass(Class> clazz) {
Class> superclass = clazz.getSuperclass();
if (superclass == null || Object.class.equals(superclass)) {//如果没有超类或者超类是Object
return null;
}
//获取超类对应的jdbcHandlerMap
Map> jdbcHandlerMap = TYPE_HANDLER_MAP.get(superclass);
if (jdbcHandlerMap != null) {//如果获取到就直接返回
return jdbcHandlerMap;
} else {//如果没有获取到,就继续递归查询上级超类对应的jdbcHandlerMap
return getJdbcHandlerMapForSuperclass(superclass);
}
}
Mybatis类型转换模块的异常处理类,比较简单,不做分析。