系列
- MyBatis攔截器原理介紹
- Mybatis攔截器改寫請求參數(shù)和結(jié)果
- Mybatis 插件兼容動態(tài)SQL
- mybatis 參數(shù)解析流程附相關(guān)案例
- 詳述mybatis執(zhí)行流程
開篇
MyBatis的 類型處理器 在設(shè)置預(yù)處理語句中的參數(shù)或從結(jié)果集中取出一個值時, 都會用類型處理器將獲取到的值以合適的方式轉(zhuǎn)換成 Java 類型。
本篇文章基于mybatis-3.5.7分支進(jìn)行分析,梳理MyBatis類型處理器在源碼層面的實(shí)現(xiàn),包含類型處理解析、請求集處理、結(jié)果集處理三個方向。
類型處理器解析
private static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {
private List<ParameterMapping> parameterMappings = new ArrayList<>();
private Class<?> parameterType;
private MetaObject metaParameters;
private ParameterMapping buildParameterMapping(String content) {
Map<String, String> propertiesMap = parseParameterMapping(content);
// 省略相關(guān)代碼
ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
Class<?> javaType = propertyType;
String typeHandlerAlias = null;
for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
String name = entry.getKey();
String value = entry.getValue();
if ("javaType".equals(name)) {
javaType = resolveClass(value);
builder.javaType(javaType);
} else if ("jdbcType".equals(name)) {
builder.jdbcType(resolveJdbcType(value));
} else if ("mode".equals(name)) {
builder.mode(resolveParameterMode(value));
} else if ("numericScale".equals(name)) {
builder.numericScale(Integer.valueOf(value));
} else if ("resultMap".equals(name)) {
builder.resultMapId(value);
// 解析對應(yīng)的typeHandler參數(shù)
} else if ("typeHandler".equals(name)) {
typeHandlerAlias = value;
} else if ("jdbcTypeName".equals(name)) {
builder.jdbcTypeName(value);
} else if ("property".equals(name)) {
// Do Nothing
} else if ("expression".equals(name)) {
// 省略相關(guān)代碼
} else {
// 省略相關(guān)代碼
}
}
// 設(shè)置對應(yīng)的typeHandlerAlias參數(shù)
if (typeHandlerAlias != null) {
builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
}
// 構(gòu)建對應(yīng)的ParameterMapping對象
return builder.build();
}
}
- 在參數(shù)解析的方法ParameterMappingTokenHandler中通過resolveTypeHandler實(shí)現(xiàn)類型處理器的解析。
- ParameterMappingTokenHandler負(fù)責(zé)生成ParameterMapping對象,里面包含typeHandler對象。
請求集處理
public class DefaultParameterHandler implements ParameterHandler {
private final TypeHandlerRegistry typeHandlerRegistry;
private final MappedStatement mappedStatement;
private final Object parameterObject;
private final BoundSql boundSql;
private final Configuration configuration;
public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
this.mappedStatement = mappedStatement;
this.configuration = mappedStatement.getConfiguration();
this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
this.parameterObject = parameterObject;
this.boundSql = boundSql;
}
@Override
public Object getParameterObject() {
return parameterObject;
}
@Override
public void setParameters(PreparedStatement ps) {
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
// 遍歷所有的參數(shù)映射
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);
}
// 獲取參數(shù)類型處理器TypeHandler對象
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 通過類型處理器進(jìn)行參數(shù)解析
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
}
}
}
}
}
}
- DefaultParameterHandler#setParameters方法會遍歷parameterMappings并調(diào)用typeHandler.setParameter的處理參數(shù)并進(jìn)行設(shè)置。
結(jié)果集處理
public interface ResultSetHandler {
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
public interface TypeHandler<T> {
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) 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;
}
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
protected Configuration configuration;
}
- 結(jié)果集處理的接口ResultSetHandler包含上述的方法,針對結(jié)果處理的方法內(nèi)會執(zhí)行類型處理器TypeHandler。
- 類型處理器TypeHandler開放的接口如上述定義,自定義類型處理器需要實(shí)現(xiàn)上述功能。
- 考慮實(shí)現(xiàn)TypeHandler的接口的復(fù)雜度較高,可以通過繼承BaseTypeHandler來簡化接口操作。
public class DefaultResultSetHandler implements ResultSetHandler {
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap,
List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
// 處理每行的結(jié)果值
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}
private void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap,
ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject,
ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
if (propertyMapping.getNestedQueryId() != null) {
return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
} else if (propertyMapping.getResultSet() != null) {
addPendingChildRelation(rs, metaResultObject, propertyMapping); // TODO is that OK?
return DEFERED;
} else {
final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
// 通過typeHandler進(jìn)行處理
return typeHandler.getResult(rs, column);
}
}
}
- 結(jié)果集的處理核心在DefaultResultSetHandler當(dāng)中,在內(nèi)部邏輯中會調(diào)用到typeHandler。
- 結(jié)果集處理的例子之一是通過typeHandler.getResult來處理結(jié)果。