在在上一章内容中我们还了关于KeyGenerator的技术债,下面还有这些技术债:
parameterHandler
resultSetHandler
今天我们就来偿还关于resultSetHandler的内容。
1. ResultSetHandler解析
首先这是一个接口,我们先来看下这个源码:
public interface ResultSetHandler {
/**
* 处理结果映射
*/
List handleResultSets(Statement stmt) throws SQLException;
/**
* 处理游标结果映射,我不太常用,不做展开
*/
Cursor handleCursorResultSets(Statement stmt) throws SQLException;
/**
* 处理存储过程结果映射,我不太常用,不做展开
*/
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
这里其实我们最常用的也就一个方法:handleResultSets。
我们再来看下它的映射关系别以为看到就一个继承方法就可以松口气,打开一看这个类吓死你~~~
不过还是要硬着头皮去看,我们还是一步一步来,首先从它的构造方法开始:
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
这个方法乍一看挺少,但是里面的调用比较深,所以看起来会很费劲,所以我们已理解整个思路为主,而不过渡关注细节。
首先我们第一个方法: ResultSetWrapper rsw = getFirstResultSet(stmt);
这我们先不做过多深入,记在技术债里,只要知道这个是封装数据库结果的。
之后在最重要的就是handleResultSet方法,我们来进入:
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List 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 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 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 mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
final List 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