执行sql,在PreparedStatement设置参数时,需要把java类型转换成jdbc类型,而从结果集中获取数据时,需要把jdbc类型转换为java类型。
所有类型转换器都继承这个接口。
public interface TypeHandler<T> {
/**
* 类型处理器:
* JAVA类型 <------> JDBC类型
* JAVA类型 ----> JDBC类型 写
* JDBC类型 ----> JAVA类型 读
* SQL操作:读 写
* 负责将Java类型转换为JDBC的类型
* 本质上执行的就是JDBC操作中的 如下操作
* String sql = "SELECT id,user_name,real_name,password,age,d_id from t_user where id = ? and user_name = ?";
* ps = conn.prepareStatement(sql);
* ps.setInt(1,2);
* ps.setString(2,"张三");
* @param ps
* @param i 对应占位符的 位置
* @param parameter 占位符对应的值
* @param jdbcType 对应的 jdbcType 类型
* @throws SQLException
*/
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
/**
* 从ResultSet中获取数据时会调用此方法,会将数据由JdbcType转换为Java类型
* ResultSet rs = ps.executeQuery();
* rs.next;
* rs.getString(columnName);
* rs.getInteger(columnIndex)
*
* @param columnName Colunm name, when configuration useColumnLabel
is false
*/
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
TypeReference的作用是获得handler的泛型的类型变量。比如IntegerTypeHandler的类型变量是java.lang.Integer。
/**
* References a generic type.
*
* @param the referenced type
* @since 3.1.0
* @author Simone Tripodi
*/
public abstract class TypeReference<T> {
private final Type rawType;
protected TypeReference() {
rawType = getSuperclassTypeParameter(getClass());
}
Type getSuperclassTypeParameter(Class<?> clazz) {
// 获得父类,包括泛型
Type genericSuperclass = clazz.getGenericSuperclass();
if (genericSuperclass instanceof Class) {
// try to climb up the hierarchy until meet something useful
// 递归往上找
if (TypeReference.class != genericSuperclass) {
return getSuperclassTypeParameter(clazz.getSuperclass());
}
throw new TypeException("'" + getClass() + "' extends TypeReference but misses the type parameter. "
+ "Remove the extension or add a type parameter to it.");
}
Type rawType = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
// TODO remove this when Reflector is fixed to return Types
if (rawType instanceof ParameterizedType) {
rawType = ((ParameterizedType) rawType).getRawType();
}
return rawType;
}
public final Type getRawType() {
return rawType;
}
@Override
public String toString() {
return rawType.toString();
}
}
BaseTypeHandler继承了TypeReference,实现TypeHandler接口。
BaseTypeHandler的实现类很多,以IntegerTypeHandler为例。
/**
* @author Clinton Begin
*/
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;
}
}
问题:类型转换器很多,如何知道用哪个具体的类型转换器。
mybatis把所有的TypeHandler注册到TypeHandlerRegistry 注册器。new Configuration类对象的时候,初始化注册器。
// 记录JdbcType和TypeHandle的对应关系
private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
// 记录Java类型向指定的JdbcType转换时需要使用到的 TypeHandle
private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
private final TypeHandler<Object> unknownTypeHandler;
// 记录全部的TypeHandle类型及对应的TypeHandle对象
private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();
// 空TypeHandle的标识
private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();
private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;
/**
* The constructor that pass the MyBatis configuration.
*
* @param configuration a MyBatis configuration
* @since 3.5.4
*/
public TypeHandlerRegistry(Configuration configuration) {
this.unknownTypeHandler = new UnknownTypeHandler(configuration);
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
register(Byte.class, new ByteTypeHandler());
register(byte.class, new ByteTypeHandler());
register(JdbcType.TINYINT, new ByteTypeHandler());
register(Short.class, new ShortTypeHandler());
register(short.class, new ShortTypeHandler());
register(JdbcType.SMALLINT, new ShortTypeHandler());
register(Integer.class, new IntegerTypeHandler());
register(int.class, new IntegerTypeHandler());
register(JdbcType.INTEGER, new IntegerTypeHandler());
register(Long.class, new LongTypeHandler());
register(long.class, new LongTypeHandler());
}
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
// 获取@MappedJdbcTypes注解
MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
if (mappedJdbcTypes != null) {
// 遍历获取注解中指定的 JdbcType 类型
for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
// 调用下一个重载的方法
register(javaType, handledJdbcType, typeHandler);
}
if (mappedJdbcTypes.includeNullJdbcType()) {
// JdbcType类型为空的情况
register(javaType, null, typeHandler);
}
} else {
register(javaType, null, typeHandler);
}
}
/**
*
* @param javaType Java 类型
* @param jdbcType Jdbc 类型
* @param handler 这两种类型对应的处理器
*/
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
if (javaType != null) {// 如果不为空
// 从 TypeHandle集合中根据Java类型来获取对应的集合
Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
if (map == null || map == NULL_TYPE_HANDLER_MAP) {
// 如果没有就创建一个新的
map = new HashMap<>();
}
// 把对应的jdbc类型和处理器添加到map集合中
map.put(jdbcType, handler);
// 然后将 java类型和上面的map集合保存到TypeHandle的容器中
typeHandlerMap.put(javaType, map);
}
// 同时也把这个处理器添加到了 保存有所有处理器的容器中
allTypeHandlersMap.put(handler.getClass(), handler);
}
/**
* 根据对应的Java类型和Jdbc类型来查找对应的TypeHandle
*/
private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
if (ParamMap.class.equals(type)) {
return null;
}
// 根据Java类型获取对应的 Jdbc类型和TypeHandle的集合容器
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
TypeHandler<?> handler = null;
if (jdbcHandlerMap != null) {
// 根据Jdbc类型获取对应的 处理器
handler = jdbcHandlerMap.get(jdbcType);
if (handler == null) {
// 获取null对应的处理器
handler = jdbcHandlerMap.get(null);
}
if (handler == null) {
// #591
handler = pickSoleHandler(jdbcHandlerMap);
}
}
// type drives generics here
return (TypeHandler<T>) handler;
}
别名注册器。
/**
* @author Clinton Begin
*/
public class TypeAliasRegistry {
// 保存 类型和别名的对应关系
private final Map<String, Class<?>> typeAliases = new HashMap<>();
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);
..............
}
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);
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() + "'.");
}
// 将 别名 和 类型 添加到 Map 集合中
typeAliases.put(key, value);
}
在Configuration的构造方法中会为各种常用的类型向TypeAliasRegistry中注册类型别名数据
TypeHandler类型处理器使用主要在两个地方:
/**
* 会通过类型处理器完成占位符的赋值操作
* @param ps
*/
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// 取出 SQL 中的参数映射列表 id int IntegerHandler
// select * from t_user where id = #{id}
// select * from t_user where id = ? 从哪去找答案?
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
// 过滤掉存储过程中的参数
Object value; // 记录实参
String propertyName = parameterMapping.getProperty(); // 获取参数名称
// 获取对应的实参值
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler(); // IntegerTypeHandler
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
// 置空的处理 setNull
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 给每一个占位符 赋值
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}