赞
踩
JDBC数据类型与Java语言中的数据类型并不是完全对应的,所以在PreparedStatement为SQL绑定参数时,需要从Java类型转换成JDBC类型,从而结果集中获取数据时,则需要从JDBC类型转换成Java类型。MyBatis使用类型处理器完成上述两种转换
MyBatis中使用JdbcType这个枚举类型代表JDBC中的数据类型,该枚举类型中定义了TYPE_CODE字段,记录了JDBC类型在java.sql.Types中相应的变量编码,并通过一个静态集合codeLoolup(Map类型)维护变量编码与JdbcType之间的关系对应
Mybatis所有的类型转换器都继承了TypeHandler接口,在TypeHandler接口中定义了4个方法。分为两类:setParameter()方法负责将数据由Java类型转为JdbcType类型;getReslut()方法及其重载负责将数据由JdbcType类型转换成Java类型
/** * Mybatis所有的类型转换器都继承了TypeHandler接口 * 用于JdbcType与Java类型之间的转换 * * @author Clinton Begin */ public interface TypeHandler<T> { /** * 在通过PreparedStatement绑定SQL参数时,会将数据由Java类型转换成JdbcType类型 * * @param ps * @param i * @param parameter * @param jdbcType * @throws SQLException */ void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException; /** * 从ResultSet中获取数据时,会将JdbcType转为Java类型 * * @param rs * @param columnName * @return * @throws SQLException */ T getResult(ResultSet rs, String columnName) throws SQLException; T getResult(ResultSet rs, int columnIndex) throws SQLException; T getResult(CallableStatement cs, int columnIndex) throws SQLException; }
为了方便用户自定一TypeHandler实现,Mybatis提供了BaseTypeHandler这个抽象类,它实现了TypeHandler接口,并继承了TypeReference抽象类。
在BaseTypeHandler中实现了setParameter()方法和getResult()方法,具体实现如下所示。需要注意的是,这两个方法对于非空数据的处理都交给子类实现。
以IntegerTypeHandler为例:
public class IntegerTypeHandler extends BaseTypeHandler<Integer> { @Override public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType) throws SQLException { // 参数绑定 ps.setInt(i, parameter); } @Override public Integer getNullableResult(ResultSet rs, String columnName) throws SQLException { // 获取指定列值 int result = rs.getInt(columnName); return result == 0 && rs.wasNull() ? null : result; } @Override public Integer getNullableResult(ResultSet rs, int columnIndex) throws SQLException { // 获取指定列值 int result = rs.getInt(columnIndex); return result == 0 && rs.wasNull() ? null : result; } @Override public Integer getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { int result = cs.getInt(columnIndex); return result == 0 && cs.wasNull() ? null : result; } }
一般情况下TypeHandler用于完成单个参数以及单个列值的类型转换,如果存在多列值转换成一个Java对象的需求,应该优先考虑使用在映射文件中定义合适的映射规则
Mybatis中有很多的TypeHandler的实现类,那边它是如何知道何时该用哪个TypeHandler的呢?在MyBatis初始化的时候,就会将所有已知TypeHandler创建对象,并实现注册到TypeHandlerRegistry中,由它来管理这些对象。
/** * 实现对众多TypeHandler的管理 * * @author Clinton Begin * @author Kazuki Shimizu */ public final class TypeHandlerRegistry { /** * 记录JdbcType与TypeHandler之间的对应管理 */ private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class); /** * 记录Java类型与JdbcType之间的对应管理,可能会有多个例如String对应可能是char,varchar */ private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>(); private final TypeHandler<Object> unknownTypeHandler; /** * 记录了全部TypeHandler的类型以及该类型相应的TypeHandler对象 */ private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>(); /** * 空TypeHandler集合的标识 */ private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap(); private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;
TypeHandlerRegister中有很多重载的register()方法。但多数的都调用了register(Type javaType, JdbcType jdbcType, TypeHandler<?> typeHandler)
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
// 检查是否明确指定了TypeHandler能够处理的Java类型
if (javaType != null) {
Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
if (map == null || map == NULL_TYPE_HANDLER_MAP) {
map = new HashMap<>();
}
map.put(jdbcType, handler);
// 将TypeHandler对象注册到typeHandlerMap中
typeHandlerMap.put(javaType, map);
}
// 向allTypeHandlersMap集合注册TypeHandler类型和对应TypeHandler对象
allTypeHandlersMap.put(handler.getClass(), handler);
}
部分的register()haiihui 读取@MappedTypes注解和@MappedJdbcType注解。都是为了向typeHandlerMap、allTypeHandlersMap集合注册TypeHandler对象。同时还可以扫描指定包下的TypeHandler实现类并完成注册。
getTypeHandler()实现了获取对应TypeHandler对象的功能。会根据指定的java类型活着jdbc类型查找想要的TypeHandler对象。还有getJdbcTypeHandlerMap()方法。
为一个类添加一个别名,通过TypeAliasRegistry完成别名注册和管理的功能,TypeAliasRegistry的结构比较简单通过一个Map类型来管理别名与Java类型之间的对应关系,通过registerAlias()方法完成注册别名。
public void registerAlias(String alias, Class<?> value) {
if (alias == null) {
throw new TypeException("The parameter alias cannot be null");
}
// 别名转换为小写
String key = alias.toLowerCase(Locale.ENGLISH);
// 监测别名是否已经存在
if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
}
// 注册别名
typeAliases.put(key, value);
}
默认为Java的基本数据类型及数字类型、基本类型的封装及其数组类型、Date等
public TypeAliasRegistry() { registerAlias("string", String.class); registerAlias("byte", Byte.class); registerAlias("long", Long.class); registerAlias("short", Short.class); registerAlias("int", Integer.class); registerAlias("integer", Integer.class); registerAlias("double", Double.class); registerAlias("float", Float.class); registerAlias("boolean", Boolean.class); registerAlias("byte[]", Byte[].class); registerAlias("long[]", Long[].class); registerAlias("short[]", Short[].class); registerAlias("int[]", Integer[].class); registerAlias("integer[]", Integer[].class); registerAlias("double[]", Double[].class); registerAlias("float[]", Float[].class); registerAlias("boolean[]", Boolean[].class); registerAlias("_byte", byte.class); registerAlias("_long", long.class); registerAlias("_short", short.class); registerAlias("_int", int.class); registerAlias("_integer", int.class); registerAlias("_double", double.class); registerAlias("_float", float.class); registerAlias("_boolean", boolean.class); registerAlias("_byte[]", byte[].class); registerAlias("_long[]", long[].class); registerAlias("_short[]", short[].class); registerAlias("_int[]", int[].class); registerAlias("_integer[]", int[].class); registerAlias("_double[]", double[].class); registerAlias("_float[]", float[].class); registerAlias("_boolean[]", boolean[].class); registerAlias("date", Date.class); registerAlias("decimal", BigDecimal.class); registerAlias("bigdecimal", BigDecimal.class); registerAlias("biginteger", BigInteger.class); registerAlias("object", Object.class); registerAlias("date[]", Date[].class); registerAlias("decimal[]", BigDecimal[].class); registerAlias("bigdecimal[]", BigDecimal[].class); registerAlias("biginteger[]", BigInteger[].class); registerAlias("object[]", Object[].class); registerAlias("map", Map.class); registerAlias("hashmap", HashMap.class); registerAlias("list", List.class); registerAlias("arraylist", ArrayList.class); registerAlias("collection", Collection.class); registerAlias("iterator", Iterator.class); registerAlias("ResultSet", ResultSet.class); }
registerAliases()还会扫描指定包下所有的类
public void registerAliases(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
// 查找指定包下的superType类型
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
for (Class<?> type : typeSet) {
// 过滤内部类、接口及抽象类
if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
registerAlias(type);
}
}
}
registerAlias()还会解析@Alias注解,来注册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);
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。