在在上一章内容中我们还了关于KeyGenerator的技术债,下面还有这些技术债:
parameterHandler
resultSetHandler
今天我们就来偿还关于resultSetHandler的内容。
1. ResultSetHandler解析
首先这是一个接口,我们先来看下这个源码:
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;
}
这里其实我们最常用的也就一个方法:handleResultSets。
我们再来看下它的映射关系 ResultSetHandler继承关系别以为看到就一个继承方法就可以松口气,打开一看这个类吓死你~~~
不过还是要硬着头皮去看,我们还是一步一步来,首先从它的构造方法开始:
public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, ParameterHandler parameterHandler, ResultHandler<?> resultHandler, BoundSql boundSql,
RowBounds rowBounds) {
this.executor = executor;
this.configuration = mappedStatement.getConfiguration();
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.parameterHandler = parameterHandler;
this.boundSql = boundSql;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
this.reflectorFactory = configuration.getReflectorFactory();
this.resultHandler = resultHandler;
}
这里都还行,我们再来看他的主要方法:handleResultSets
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
//获取数据库结果,并装饰了下
ResultSetWrapper rsw = getFirstResultSet(stmt);
//获取自己配置的ResultMap
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
//检查rsw与resultMapCount是否合理
validateResultMapsCount(rsw, resultMapCount);
//这里逻辑是遍历resultMaps或rsw,一般来说不是存储过程我们的返回结果只有一个Object
//这里的理解是一个Object可能是List,包含多条记录
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
//跟上述逻辑相似
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
这个方法乍一看挺少,但是里面的调用比较深,所以看起来会很费劲,所以我们已理解整个思路为主,而不过渡关注细节。
首先我们第一个方法: ResultSetWrapper rsw = getFirstResultSet(stmt);
这我们先不做过多深入,记在技术债里,只要知道这个是封装数据库结果的。
之后在最重要的就是handleResultSet方法,我们来进入:
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 {
//对应resultMap进入此处
if (resultHandler == null) {
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
//这边个人感觉除了存储过程是不会进来的,因为之前说了rsw一个只有一个值,而resultHandler大多数不指定,初始都会null
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}
这里会跳到我们下一个关键方法handleRowValues来处理行数据:
public 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 void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
ResultSet resultSet = rsw.getResultSet();
//根据rowBounds选定相应的值,这里看出我们值分页都是在应用层而非数据库层
skipRows(resultSet, rowBounds);
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
// 根据该行记录以及 ResultMap.discriminator ,决定映射使用的 ResultMap 对象
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
// 根据最终确定的 ResultMap 对 ResultSet 中的该行记录进行映射,得到映射后的结果对象
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
// 将映射创建的结果对象添加到 ResultHandler.resultList 中保存
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
- resolveDiscriminatedResultMap方法我们在一般使用中不会进入,所以不进行深入分析
- getRowValue 是实际映射行数据,所以我们进行重点查看
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
// 创建映射后的结果对象,一般是初始化,还没赋值
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
//判断是否开启自动映射功能,默认不开启嵌套的自动映射
if (shouldApplyAutomaticMappings(resultMap, false)) {
//自动映射未明确的列
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
//映射 ResultMap 中明确映射的列
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
这里的步骤就和我们自己创建一个结果映射对象的顺序差不多了:
- 首先实例化一个结果类
- 开始根据结果插入进行对应的数据库值
那么下面我们就来看他是如何实现这个的自动化的。
先来看下如果开启了自动化映射功能之后applyAutomaticMappings的方法:
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
// 获得 UnMappedColumnAutoMapping 数组
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (!autoMapping.isEmpty()) {
for (UnMappedColumnAutoMapping mapping : autoMapping) {
//从resultSet中获取对应column对应的值
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
// gcode issue #377, call setter on nulls (value is not 'found')
//给对应映射对象赋值
metaObject.setValue(mapping.property, value);
}
}
}
return foundValues;
}
这里关于createAutomaticMappings方法的逻辑我们也不深入了,我们来看下UnMappedColumnAutoMapping理解他干了哪些就行:
private static class UnMappedColumnAutoMapping {
/**
* 字段名
*/
private final String column;
/**
* 属性名
*/
private final String property;
/**
* TypeHandler 处理器
*/
private final TypeHandler<?> typeHandler;
/**
* 是否为基本属性
*/
private final boolean primitive;
public UnMappedColumnAutoMapping(String column, String property, TypeHandler<?> typeHandler, boolean primitive) {
this.column = column;
this.property = property;
this.typeHandler = typeHandler;
this.primitive = primitive;
}
}
这里理解一下逻辑,遍历未 mapped 的字段的名字的数组,映射每一个字段在结果对象的相同名字的属性,最终生成 UnMappedColumnAutoMapping 对象。
看到自动化映射之后,我们再找来看下默认的映射方式applyPropertyMappings:
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
// 获得 mapped 的字段的名字的数组
final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
if (propertyMapping.getNestedResultMapId() != null) {
// the user added a column attribute to a nested result map, ignore it
column = null;
}
if (propertyMapping.isCompositeResult()
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
|| propertyMapping.getResultSet() != null) {
//获取对应的值
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
// issue #541 make property optional
final String property = propertyMapping.getProperty();
if (property == null) {
continue;
} else if (value == DEFERRED) {
foundValues = true;
continue;
}
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
// gcode issue #377, call setter on nulls (value is not 'found')
metaObject.setValue(property, value);
}
}
}
return foundValues;
}
这个方法的逻辑比较简单,通过我们设置的resultMap配置来获取数据库对应的值并映射进去。这里不管是什么方式,我们可以看到映射方法都是通过metaObject.setValue(property, value);
上面还有一个比较关键的方法getPropertyMappingValue:
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 DEFERRED;
} else {
//直接获取
final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
return typeHandler.getResult(rs, column);
}
}
2.今日总结
今天我们主要分析的是MyBatis是如何自动映射结果对象的,设计到的过程也相对比较负责,中心思想就是通过resultMap的配置,去数据库取,并通过MeteObject辅助类反射进去值。
这里我们又欠下了关于ResultSetWrapper的技术债,我们现在来整理下:
parameterHandler
ResultSetWrapper
网友评论