TypeHandler
因为jdbc中数据的类型和java的数据类型并不是直接对应的,因此需要在jdbc数据类型和java数据类型之间进行转换,转换主要发生在两个场景:
- 当给sql语句注入参数时,需要将java数据类型转换成jdbc数据类型
- 当从数据库中查询出记录后,需要将对应的记录转换成DO时,需要将jdbc数据类型转换成jdbc数据类型
而TypeHandler就是用来进行jdbc数据类型和java数据类型之间的转换的
接口方法
首先看下TypeHandler这个接口都有哪些方法
public interface TypeHandler<T> {
// 将参数从java数据类型转化成jdbc数据类型,然后绑定到sql上
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
/**
* Gets the result.
*
* @param rs
* the rs
* @param columnName
* Colunm name, when configuration <code>useColumnLabel</code> is <code>false</code>
* @return the result
* @throws SQLException
* the SQL exception
*/
/**
* 下面的多个方法将从数据库中读取的列数据转换成java数据类型
*/
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
BaseTypeHandler
BaseTypeHandler是实现了TypeHandler接口的一个简单抽象类
下面看下它是如何实现方法的
setParameter
setParameter的实现中完成了对null值的处理,将非null值的处理方法抽到setNonNullParameter中,由子类来实现
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
if (jdbcType == null) {
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException e) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . "
+ "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuBaration property. "
+ "Cause: " + e, e);
}
} else {
try {
setNonNullParameter(ps, i, parameter, jdbcType);
} catch (Exception e) {
throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . "
+ "Try setting a different JdbcType for this parameter or a different configuration property. "
+ "Cause: " + e, e);
}
}
}
getResult
只是简单地对异常进行了一次转换,将具体的实现抽到getNullableResult中,由子类来实现
@Override
public T getResult(ResultSet rs, String columnName) throws SQLException {
try {
return getNullableResult(rs, columnName);
} catch (Exception e) {
throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e);
}
}
子类BooleanTypeHandler
这里简单地看下子类BooleanTypeHandler,比较简单,主要就是调用PreparedStatement的方法
public class BooleanTypeHandler extends BaseTypeHandler<Boolean> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Boolean parameter, JdbcType jdbcType)
throws SQLException {
ps.setBoolean(i, parameter);
}
@Override
public Boolean getNullableResult(ResultSet rs, String columnName)
throws SQLException {
boolean result = rs.getBoolean(columnName);
return !result && rs.wasNull() ? null : result;
}
@Override
public Boolean getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
boolean result = rs.getBoolean(columnIndex);
return !result && rs.wasNull() ? null : result;
}
@Override
public Boolean getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
boolean result = cs.getBoolean(columnIndex);
return !result && cs.wasNull() ? null : result;
}
}
TypeHandlerRegister
TypeHandlerRegister用来注册TypeHandler
构造函数
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());
// 省略下面的,都是对一些handler的注册
构造函数中主要就是注册一些默认的TypeHandler
另外需要看几个比较重要的map
private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();
上面的三个map具体是干嘛的,通过他们的泛型和变量名称非常容易理解,下面通过register来注册typehandler的时候,会更新这些map
register
基于java类型的注册
可以看到有两个版本的register,一种是需要指定jdbc数据类型的,另外一种则是不需要指定jdbc类型的
当调用了不需要指定Jdbc类型版本时,会判断注册的handler上是否使用了MappedJdbcTypes注解,如果使用了该注解,那么会读取该注解的配置信息,从而解析出当前需要注册的jdbc数据类型
public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler) {
register((Type) javaType, typeHandler);
}
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
// 从typehandler的MappedJdbcTypes注解上读取当前需要注册的jdbc数据类型
MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
if (mappedJdbcTypes != null) {
for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
register(javaType, handledJdbcType, typeHandler);
}
if (mappedJdbcTypes.includeNullJdbcType()) {
register(javaType, null, typeHandler);
}
} else {
register(javaType, null, typeHandler);
}
}
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
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);
typeHandlerMap.put(javaType, map);
}
allTypeHandlersMap.put(handler.getClass(), handler);
}
另外如果同时指定了jdbc类型和java类型,那么调用的也是这里的方法
基于jdbc类型的注册
public void register(JdbcType jdbcType, TypeHandler<?> handler) {
jdbcTypeHandlerMap.put(jdbcType, handler);
}
getTypeHandler
getTypeHandler主要是根据传入的参数来获取相应的typehandler
getTypeHandler有多个重载版本,但是最终都会调用下面这个版本
private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
if (ParamMap.class.equals(type)) {
return null;
}
// 获取当前java类型的用来获取转换到不同jdbctype的handler的map
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
TypeHandler<?> handler = null;
if (jdbcHandlerMap != null) {
handler = jdbcHandlerMap.get(jdbcType);
if (handler == null) {
handler = jdbcHandlerMap.get(null);
}
if (handler == null) {
// #591
// 如果只注册了一个typehandler,那么会返回这个typehandler
handler = pickSoleHandler(jdbcHandlerMap);
}
}
// type drives generics here
return (TypeHandler<T>) handler;
}