MyBatis 源码分析 - SQL执行过程(三)之 ResultSetHandler
参考 知识星球 中 芋道源码 星球的源码解析,一个活跃度非常高的 Java 技术社群,感兴趣的小伙伴可以加入 芋道源码 星球,一起学习😄
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址、Mybatis-Spring 源码分析 GitHub 地址、Spring-Boot-Starter 源码分析 GitHub 地址)进行阅读
MyBatis 版本:3.5.2
MyBatis-Spring 版本:2.0.3
MyBatis-Spring-Boot-Starter 版本:2.1.4
该系列其他文档请查看:《精尽 MyBatis 源码分析 - 文章导读》
MyBatis的SQL执行过程
在前面一系列的文档中,我已经分析了 MyBatis 的基础支持层以及整个的初始化过程,此时 MyBatis 已经处于就绪状态了,等待使用者发号施令了
那么接下来我们来看看它执行SQL的整个过程,该过程比较复杂,涉及到二级缓存,将返回结果转换成 Java 对象以及延迟加载等等处理过程,这里将一步一步地进行分析:
MyBatis中SQL执行的整体过程如下图所示:
在 SqlSession 中,会将执行 SQL 的过程交由Executor
执行器去执行,过程大致如下:
- 通过
DefaultSqlSessionFactory
创建与数据库交互的SqlSession
“会话”,其内部会创建一个Executor
执行器对象 - 然后
Executor
执行器通过StatementHandler
创建对应的java.sql.Statement
对象,并通过ParameterHandler
设置参数,然后执行数据库相关操作 - 如果是数据库更新操作,则可能需要通过
KeyGenerator
先设置自增键,然后返回受影响的行数 - 如果是数据库查询操作,则需要将数据库返回的
ResultSet
结果集对象包装成ResultSetWrapper
,然后通过DefaultResultSetHandler
对结果集进行映射,最后返回 Java 对象
上面还涉及到一级缓存、二级缓存和延迟加载等其他处理过程
SQL执行过程(三)之ResultSetHandler
高能预警 ❗ ❗ ❗ ❗ ❗ ❗
DefaultResultSetHandler(结果集处理器)将数据库查询结果转换成 Java 对象是一个非常繁琐的过程,需要处理各种场景,如果继续往下看,请做好心理准备😄😄
可以先跳转到 DefaultResultSetHandler,查看流程图
在前面SQL执行过程一系列的文档中,已经详细地分析了在MyBatis的SQL执行过程中,SqlSession会话将数据库操作交由Executor执行器去完成,然后通过StatementHandler去执行数据库相关操作,并获取到数据库的执行结果
如果是数据库查询操作,则需要通过ResultSetHandler
对查询返回的结果集进行映射处理,转换成对应的Java对象,算是SQL执行过程的最后一步,那么我们来看看MyBatis是如何完成这个繁杂的解析过程的
ResultSetHandler接口的实现类如下图所示:
先回顾一下ResultSetHandler
在哪被调用,在PreparedStatementHandler
的query
方法中,代码如下:
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 执行
ps.execute();
// 结果处理器并返回结果
return resultSetHandler.handleResultSets(ps);
}
- 属性
resultSetHandler
默认为DefaultResultSetHandler
对象,可以回到《SQL执行过程(二)之StatementHandler》的BaseStatementHandler小节中的构造方法
的第3
步可以看到 - 调用
resultSetHandler
的handleResultSets(Statement stmt)
方法,对结果集进行映射,转换成Java对象并返回
ResultSetWrapper
因为在
DefaultResultSetHandler
中,对ResultSet
的操作更多的是它的ResultSetWrapper
包装类,所以我们先来看看这个类
org.apache.ibatis.executor.resultset.ResultSetWrapper
:java.sql.ResultSet
的包装类,为DefaultResultSetHandler
提供许多便捷的方法,直接来看它的代码
构造方法
public class ResultSetWrapper {
/**
* ResultSet 对象
*/
private final ResultSet resultSet;
/**
* 类型处理器注册表
*/
private final TypeHandlerRegistry typeHandlerRegistry;
/**
* ResultSet 中每列的列名
*/
private final List<String> columnNames = new ArrayList<>();
/**
* ResultSet 中每列对应的 Java Type
*/
private final List<String> classNames = new ArrayList<>();
/**
* ResultSet 中每列对应的 Jdbc Type
*/
private final List<JdbcType> jdbcTypes = new ArrayList<>();
/**
* 记录每列对应的 TypeHandler 对象
* key:列名
* value:TypeHandler 集合
*/
private final Map<String, Map<Class<?>, TypeHandler<?>>> typeHandlerMap = new HashMap<>();
/**
* 记录了被映射的列名
* key:ResultMap 对象的 id {@link #getMapKey(ResultMap, String)}
* value:ResultMap 对象映射的列名集合
*/
private final Map<String, List<String>> mappedColumnNamesMap = new HashMap<>();
/**
* 记录了未映射的列名
* key:ResultMap 对象的 id {@link #getMapKey(ResultMap, String)}
* value:ResultMap 对象未被映射的列名集合
*/
private final Map<String, List<String>> unMappedColumnNamesMap = new HashMap<>();
public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
super();
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.resultSet = rs;
// 获取 ResultSet 的元信息
final ResultSetMetaData metaData = rs.getMetaData();
final int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
// 获得列名或者通过 AS 关键字指定列名的别名
columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));
// 获得该列对应的 Jdbc Type
jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
// 获得该列对应的 Java Type
classNames.add(metaData.getColumnClassName(i));
}
}
}
resultSet
:被包装的ResultSet
结果集对象typeHandlerRegistry
:类型处理器注册表,因为需要进行Java Type与Jdbc Type之间的转换columnNames
:结果集中的所有列名classNames
:结果集中的每列的对应的Java Type的名称jdbcTypes
:结果集中的每列对应的Jdbc TypetypeHandlerMap
:结果集中每列对应的类型处理器mappedColumnNamesMap
:保存每个ResultMap对象中映射的列名集合,也就是我们在<resultMap />
标签下的子标签配置的column
属性unMappedColumnNamesMap
:保存每个ResultMap对象中未映射的列名集合,也就是没有在<resultMap />
标签下配置过,但是查询结果返回了
在构造方法中,会初始化上面的columnNames
、classNames
和jdbcTypes
属性
getTypeHandler方法
getTypeHandler(Class<?> propertyType, String columnName)
:通过列名和Java Type获取对应的TypeHandler
类型处理器,方法如下:
public TypeHandler<?> getTypeHandler(Class<?> propertyType, String columnName) {
TypeHandler<?> handler = null;
// 获取列名对应的类型处理器
Map<Class<?>, TypeHandler<?>> columnHandlers = typeHandlerMap.get(columnName);
if (columnHandlers == null) {
columnHandlers = new HashMap<>();
typeHandlerMap.put(columnName, columnHandlers);
} else {
handler = columnHandlers.get(propertyType);
}
if (handler == null) {
// 获取该列对应的 Jdbc Type
JdbcType jdbcType = getJdbcType(columnName);
// 根据 Java Type 和 Jdbc Type 获取对应的 TypeHandler 类型处理器
handler = typeHandlerRegistry.getTypeHandler(propertyType, jdbcType);
// Replicate logic of UnknownTypeHandler#resolveTypeHandler
// See issue #59 comment 10
if (handler == null || handler instanceof UnknownTypeHandler) {
// 从 ResultSet 中获取该列对应的 Java Type 的 Class 对象
final int index = columnNames.indexOf(columnName);
final Class<?> javaType = resolveClass(classNames.get(index));
if (javaType != null && jdbcType != null) {
handler = typeHandlerRegistry.getTypeHandler(javaType, jdbcType);
} else if (javaType != null) {
handler = typeHandlerRegistry.getTypeHandler(javaType);
} else if (jdbcType != null) {
handler = typeHandlerRegistry.getTypeHandler(jdbcType);
}
}
if (handler == null || handler instanceof UnknownTypeHandler) {
// 最差的情况,设置为 ObjectTypeHandler
handler = new ObjectTypeHandler();
}
// 将生成的 TypeHandler 存放在 typeHandlerMap 中
columnHandlers.put(propertyType, handler);
}
return handler;
}
大致逻辑如下:
- 先从
Map<String, Map<Class<?>, TypeHandler<?>>> typeHandlerMap
属性中获取类型处理器 - 如果从缓存中没有获取到,则尝试根据Jdbc Type和Java Type从
typeHandlerRegistry
注册表获取 - 如果还是没有获取到,则根据
classNames
中拿到结果集中该列的Java Type,然后在从typeHandlerRegistry
注册表获取 - 还是没有获取到,则设置为
ObjectTypeHandler
- 最后将其放入
typeHandlerMap
缓存中
loadMappedAndUnmappedColumnNames方法
loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix)
方法,初始化mappedColumnNamesMap
和unMappedColumnNamesMap
两个属性,分别为映射的列名和未被映射的列名,方法如下:
private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
List<String> mappedColumnNames = new ArrayList<>();
List<String> unmappedColumnNames = new ArrayList<>();
// <1> 获取配置的列名的前缀,全部大写
final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);
/*
* <2> 获取 ResultMap 中配置的所有列名,并添加前缀
* 如果在 <select /> 上面配置的是 resultType 属性,则返回的是空集合,因为它生成的 ResultMap 只有 Java Type 属性
*/
final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);
/*
* <3> 遍历数据库查询结果中所有的列名
* 将所有列名分为两类:是否配置了映射
*/
for (String columnName : columnNames) {
final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);
if (mappedColumns.contains(upperColumnName)) {
mappedColumnNames.add(upperColumnName);
} else {
unmappedColumnNames.add(columnName);
}
}
// <4> 将上面两类的列名保存
mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);
unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);
}
-
获取配置的列名的前缀,全部大写,通常是没有配置的
-
获取
ResultMap
中配置的所有列名,并添加前缀如果在
<select />
上面配置的是resultType
属性,则返回的是空集合,因为它创建的ResultMap
对象中只有Java Type属性 -
遍历结果集中所有的列名,如果在
<resultMap />
标签中的子标签配置的column
属性有包含这个列名,则属于映射的列名 -
否则就属于未被映射的列名
ResultSetHandler
org.apache.ibatis.executor.resultset.ResultSetHandler
:结果集映射接口,代码如下:
public interface ResultSetHandler {
/**
* 处理 {@link java.sql.ResultSet} 成映射的对应的结果
*
* @param stmt Statement 对象
* @param <E> 泛型
* @return 结果数组
* @throws SQLException SQL异常
*/
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
/**
* 处理 {@link java.sql.ResultSet} 成 Cursor 对象
*
* @param stmt Statement 对象
* @param <E> 泛型
* @return Cursor 对象
* @throws SQLException SQL异常
*/
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
/**
* 暂时忽略,和存储过程相关
*
* @param cs CallableStatement 对象
* @throws SQLException SQL异常
*/
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
DefaultResultSetHandler
org.apache.ibatis.executor.resultset.DefaultResultSetHandler
:实现ResultSetHandler接口,处理数据库的查询结果,对结果集进行映射,将结果转换成Java对象
由于该类嵌套的方法太多了,可能一个方法会有十几层的嵌套,所以本分不会进行全面的分析
因为我查看这个类的时候是从下面的方法一层一层往上看的,注释我全部添加了,所以可以参考我的注释一步一步查看
接下来的描述可能有点混乱,请按照我在方法前面表明的顺序进行查看,参考:DefaultResultSetHandler.java
先来看下DefaultResultSetHandler
处理结果集的方法的流程图:
构造方法
public class DefaultResultSetHandler implements ResultSetHandler {
/**
* 延迟加载默认对象
*/
private static final Object DEFERRED = new Object();
/**
* 执行器
*/
private final Executor executor;
/**
* 全局配置对象
*/
private final Configuration configuration;
/**
* 本次查询操作对应的 MappedStatement 对象
*/
private final MappedStatement mappedStatement;
/**
* 分页对象
*/
private final RowBounds rowBounds;
/**
* 参数处理器,默认为 DefaultParameterHandler
*/
private final ParameterHandler parameterHandler;
/**
* 结果处理器,默认为 DefaultResultHandler
*/
private final ResultHandler<?> resultHandler;
/**
* SQL 相关信息
*/
private final BoundSql boundSql;
/**
* 类型处理器注册表
*/
private final TypeHandlerRegistry typeHandlerRegistry;
/**
* 对象实例工厂
*/
private final ObjectFactory objectFactory;
/**
* Reflector 工厂
*/
private final ReflectorFactory reflectorFactory;
// nested resultmaps
private final Map<CacheKey, Object> nestedResultObjects = new HashMap<>();
private final Map<String, Object> ancestorObjects = new HashMap<>();
private Object previousRowValue;
// multiple resultsets
private final Map<String, ResultMapping> nextResultMaps = new HashMap<>();
private final Map<CacheKey, List<PendingRelation>> pendingRelations = new HashMap<>();
// Cached Automappings
private final Map<String, List<UnMappedColumnAutoMapping>> autoMappingsCache = new HashMap<>();
// temporary marking flag that indicate using constructor mapping (use field to reduce memory usage)
private boolean useConstructorMappings;
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;
}
}
- 上面的属性有点多,可以先根据注释进行理解,也可以在接下来的方法中逐步理解
1.handleResultSets方法
handleResultSets(Statement stmt)
方法,处理结果集的入口
/**
* 1.处理结果集
*/
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
/*
* <1> 用于保存映射结果集得到的结果队形
* 多 ResultSet 的结果集合,每个 ResultSet 对应一个 Object 对象,而实际上,每个 Object 是 List<Object> 对象
*/
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
// <2> 获取 ResultSet 对象,并封装成 ResultSetWrapper
ResultSetWrapper rsw = getFirstResultSet(stmt);
/*
* <3> 获得当前 MappedStatement 对象中的 ResultMap 集合,XML 映射文件中 <resultMap /> 标签生成的
* 或者 配置 "resultType" 属性也会生成对应的 ResultMap 对象
* 在 <select /> 标签配置 ResultMap 属性时,可以以逗号分隔配置多个,如果返回多个 ResultSet 则会一一映射,通常配置一个
*/
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
// <4> 如果有返回结果,但是没有 ResultMap 接收对象则抛出异常
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
/*
* <5> 完成结果集的映射,全部转换的 Java 对象
* 保存至 multipleResults 集合中,或者 this.resultHandler 中
*/
handleResultSet(rsw, resultMap, multipleResults, null);
// 获取下一个结果集
rsw = getNextResultSet(stmt);
// 清空 nestedResultObjects 集合
cleanUpAfterHandlingResultSet();
// 递增 resultSetCount 结果集数量
resultSetCount++;
}
// <6> 获取 resultSets 多结果集属性的配置,存储过程中使用,暂时忽略
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
// 根据 resultSet 的名称,获取未处理的 ResultMapping
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
// 未处理的 ResultMap 对象
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
// 完成结果集的映射,全部转换的 Java 对象
handleResultSet(rsw, resultMap, null, parentMapping);
}
// 获取下一个结果集
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
// <7> 如果是 multipleResults 单元素,则取首元素返回
return collapseSingleResultList(multipleResults);
}
-
multipleResults
用于保存映射结果集得到的结果队形,多 ResultSet 的结果集合,每个 ResultSet 对应一个 Object 对象,而实际上,每个 Object 是List<Object>
对象 -
获取
ResultSet
对象,并封装成ResultSetWrapper
-
获得当前 MappedStatement 对象中的
ResultMap
集合,XML 映射文件中<resultMap />
标签生成的,或者 配置 "resultType" 属性也会生成对应的ResultMap
对象在
<select />
标签配置ResultMap
属性时,可以以逗号分隔配置多个,如果返回多个 ResultSet 则会一一映射,通常配置一个 -
如果有返回结果,但是没有
ResultMap
接收对象则抛出异常 -
调用
handleResultSet
方法,完成结果集的映射,全部转换的 Java 对象,保存至multipleResults
集合中,或者this.resultHandler
中(用户自定的,通常不会) -
获取 resultSets 多结果集属性的配置,存储过程中使用,暂时忽略,本文暂不分析
完成结果集映射的任务还是交给了2.handleResultSet方法
2.handleResultSet方法
handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping)
方法,处理结果集
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults,
ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
// <1> 暂时忽略,因为只有存储过程的情况时 parentMapping 为非空
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) { // <2>
// <2.1> 创建 DefaultResultHandler 默认结果处理器
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
// <2.2> 处理结果集,进行一系列的处理,完成映射,将结果保存至 DefaultResultHandler 中
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
// <2.3> 将结果集合添加至 multipleResults 中
multipleResults.add(defaultResultHandler.getResultList());
} else { // 用户自定义了 resultHandler,则结果都会保存在其中
// <3> 处理结果集,进行一系列的处理,完成映射,将结果保存至 DefaultResultHandler 中
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
// <4> 关闭结果集
closeResultSet(rsw.getResultSet());
}
}
-
暂时忽略,因为只有存储过程的情况时 parentMapping 为非空,查看上面的1.handleResultSets方法的第
6
步 -
用户没有指定
ResultHandler
结果处理器- 创建
DefaultResultHandler
默认结果处理器,就是使用一个List集合保存转换后的Java对象 - 调用
handleRowValues
方法,处理结果集,进行一系列的处理,完成映射,将结果保存至 DefaultResultHandler 中 - 将结果集合添加至
multipleResults
中
- 创建
-
用户指定了自定义的
ResultHandler
结果处理器,和第2
步的区别在于,处理后的Java对象不会保存在multipleResults
中,仅保存在ResultHandler
中,用户可通过它获取 -
关闭 ResultSet 结果集对象
通常我们不会自定义结果处理器的,所以第4
步本文暂不分析,我们来看到第2
步,最终还是交给了3.handleRowValues方法
3.handleRowValues方法
handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
方法,处理结果集
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler,
RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
/*
* <1> ResultMap 存在内嵌的 ResultMap
* 例如 <resultMap /> 标签中 <association /> 或者 <collection /> 都会创建对应的 ResultMap 对象
* 该对象的 id 会设置到 ResultMapping 的 nestedResultMapId 属性中,这就属于内嵌的 ResultMap
*/
if (resultMap.hasNestedResultMaps()) { // 存在
// <1.1> 如果不允许在嵌套语句中使用分页,则对 rowBounds 进行校验,设置了 limit 或者 offset 则抛出异常,默认允许
ensureNoRowBounds();
// <1.2> 校验要不要使用自定义的 ResultHandler,针对内嵌的 ResultMap
checkResultHandler();
// <1.3> 处理结果集,进行映射,生成返回结果,保存至 resultHandler 或者设置到 parentMapping 的对应属性中
// 这里会处理内嵌的 ResultMap
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
// <2> 处理结果集,进行映射,生成返回结果,保存至 resultHandler 或者设置到 parentMapping 的对应属性中
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
-
如果当前 ResultMap 存在内嵌的 ResultMap
例如
<resultMap />
标签中<association />
或者<collection />
都会创建对应的 ResultMap 对象,该对象的 id 会设置到ResultMapping
的nestedResultMapId
属性中,这就属于内嵌的 ResultMap- 如果不允许在嵌套语句中使用分页,则对 rowBounds 进行校验,设置了 limit 或者 offset 则抛出异常,默认允许
- 校验要不要使用自定义的 ResultHandler,针对内嵌的 ResultMap
- 处理结果集,进行映射,生成返回结果,保存至
resultHandler
或者设置到parentMapping
(存储过程相关,本文暂不分析)的对应属性中,这里会对内嵌的 ResultMap 进行处理,调用handleRowValuesForNestedResultMap
方法
-
处理结果集,进行映射,生成返回结果,保存至
resultHandler
或者设置到parentMapping
(存储过程相关,本文暂不分析)的对应属性中,调用handleRowValuesForSimpleResultMap
方法
这里先来看到第2
步中的4.handleRowValuesForSimpleResultMap方法,因为这个处理的情况相比第1
步调用的方法简单些
4.handleRowValuesForSimpleResultMap方法
handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping 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();
// <1> 根据 RowBounds 中的 offset 跳到到指定的记录
skipRows(resultSet, rowBounds);
// <2> 检测已经处理的行数是否已经达到上限(RowBounds.limit)以及 ResultSet 中是否还有可处理的记录
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
/*
* <3> 获取最终的 ResultMap
* 因为 ResultMap 可能使用到了 <discriminator /> 标签,需要根据不同的值映射不同的 ResultMap
* 如果存在 Discriminator 鉴别器,则根据当前记录选择对应的 ResultMap,会一直嵌套处理
*/
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
// <4> 从结果集中获取到返回结果对象,进行映射,比较复杂,关键方法!!!
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
// <5> 将返回结果对象保存至 resultHandler,或者设置到父对象 parentMapping 的对应属性中
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
这里创建了一个DefaultResultContext
保存结果的上下文对象,点击去你会发现有3个属性:
resultObject
:暂存映射后的返回结果,因为结果集中可能有很多条数据resultCount
:记录经过DefaultResultContext
暂存的对象个数stopped
:控制是否还进行映射
-
根据
RowBounds
中的offset
跳到到结果集中指定的记录 -
检测已经处理的行数是否已经达到上限(
RowBounds.limit
)以及ResultSet
中是否还有可处理的记录 -
调用
resolveDiscriminatedResultMap
方法,获取最终的 ResultMap因为 ResultMap 可能使用到了
<discriminator />
标签,需要根据不同的值映射不同的 ResultMap
如果存在Discriminator
鉴别器,则根据当前记录选择对应的 ResultMap,会一直嵌套处理 -
调用
getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix)
方法,从结果集中获取到返回结果对象,进行映射,比较复杂,关键方法!!! -
调用
storeObject
方法,将返回结果对象保存至resultHandler
,或者设置到父对象parentMapping
(存储过程相关,本文暂不分析)的对应属性中
对于第3
、4
、5
步的三个方法,我们一个一个来看
-
4.1resolveDiscriminatedResultMap方法
-
4.2getRowValue方法
-
4.3storeObject方法
4.1resolveDiscriminatedResultMap方法
resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix)
方法,如果存在<discriminator />
鉴别器,则进行处理,选择对应的 ResultMap
,会一直嵌套处理
public ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix)
throws SQLException {
// 记录已经处理过的 ResultMap 的 id
Set<String> pastDiscriminators = new HashSet<>();
// <1> 获取 ResultMap 中的 Discriminator 鉴别器,<discriminator />标签会被解析成该对象
Discriminator discriminator = resultMap.getDiscriminator();
while (discriminator != null) {
// <2> 获取当前记录中该列的值,通过类型处理器转换成了对应的类型
final Object value = getDiscriminatorValue(rs, discriminator, columnPrefix);
// <3> 鉴别器根据该值获取到对应的 ResultMap 的 id
final String discriminatedMapId = discriminator.getMapIdFor(String.valueOf(value));
if (configuration.hasResultMap(discriminatedMapId)) {
// <3.1> 获取到对应的 ResultMap
resultMap = configuration.getResultMap(discriminatedMapId);
// <3.2> 记录上一次的鉴别器
Discriminator lastDiscriminator = discriminator;
// <3.3> 获取到对应 ResultMap 内的鉴别器,可能鉴别器里面还有鉴别器
discriminator = resultMap.getDiscriminator();
// <3.4> 检测是否出现循环嵌套了
if (discriminator == lastDiscriminator || !pastDiscriminators.add(discriminatedMapId)) {
break;
}
} else {
// <4> 鉴别结果没有对应的 ResultMap,则直接跳过
break;
}
}
// <5> 返回最终使用的 ResultMap 对象
return resultMap;
}
-
获取 ResultMap 中的
Discriminator
鉴别器,<discriminator />
标签会被解析成该对象 -
调用
getDiscriminatorValue
方法,获取当前记录中该列的值,通过类型处理器转换成了对应的类型,方法如下:private Object getDiscriminatorValue(ResultSet rs, Discriminator discriminator, String columnPrefix) throws SQLException { // 获取 <discriminator />标签对应的的 ResultMapping 对象 final ResultMapping resultMapping = discriminator.getResultMapping(); // 获取 TypeHandler 类型处理器 final TypeHandler<?> typeHandler = resultMapping.getTypeHandler(); // 通过 TypeHandler 从 ResultSet 中获取该列的值 return typeHandler.getResult(rs, prependPrefix(resultMapping.getColumn(), columnPrefix)); }
-
Discriminator
鉴别器根据该值获取到对应的 ResultMap 的 id- 存在对应的 ResultMap 对象,则获取到
- 记录上一次的鉴别器
- 获取到对应 ResultMap 内的鉴别器,可能鉴别器里面还有鉴别器
- 检测是否出现循环嵌套了
-
Discriminator
鉴别结果没有对应的 ResultMap,则直接跳过 -
返回最终使用的 ResultMap 对象
4.2getRowValue方法
getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix)
方法,处理结果集
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
// <1> 保存延迟加载的集合
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
// <2> 创建返回结果的实例对象(如果存在嵌套子查询且是延迟加载则为其创建代理对象,后续的延迟加载保存至 lazyLoader 中即可)
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
/*
* <3> 如果上面创建的返回结果的实例对象不为 null,并且没有对应的 TypeHandler 类型处理器,则需要对它进行赋值
* 例如我们返回结果为 java.lang.String 就不用了,因为上面已经处理且赋值了
*/
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
// <3.1> 将返回结果的实例对象封装成 MetaObject,便于操作
final MetaObject metaObject = configuration.newMetaObject(rowValue);
// <3.2> 标记是否成功映射了任意一个属性,useConstructorMappings 表示是否在构造方法中使用了参数映射
boolean foundValues = this.useConstructorMappings;
// <3.3> 检测是否需要自动映射
if (shouldApplyAutomaticMappings(resultMap, false)) {
/*
* <3.4> 从结果集中将未被映射的列值设置到返回结果 metaObject 中
* 返回是否映射成功,设置了1个或以上的属性值
*/
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
/*
* <3.5> 从结果集中将 ResultMap 中需要映射的列值设置到返回结果 metaObject 中
* 返回是否映射成功,设置了1个或以上的属性值
*/
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
/*
* <3.6> 如果没有成功映射任意一个属性,则根据 returnInstanceForEmptyRow 全局配置(默认为false)返回空对象还是 null
*/
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
// <4> 返回该结果对象
return rowValue;
}
-
创建一个保存延迟加载的集合
ResultLoaderMap
对象lazyLoader
,如果存在代理对象,创建的代理对象则需要通过它来执行需要延迟加载的方法,在后续会将到😈 -
调用
createResultObject
方法,创建返回结果的实例对象rowValue
(如果存在嵌套子查询且是延迟加载则为其创建代理对象,后续的延迟加载保存至lazyLoader
中即可) -
如果上面创建的返回结果的实例对象
rowValue
不为 null,并且没有对应的 TypeHandler 类型处理器,则需要对它进行赋值例如我们返回结果为 java.lang.String 就不用了,因为上面已经处理且赋值了
- 将返回结果的实例对象封装成 MetaObject 对象
metaObject
,便于操作 - 标记是否成功映射了任意一个属性,
useConstructorMappings
表示是否在构造方法中使用了参数映射 - 调用
shouldApplyAutomaticMappings
方法,检测是否需要自动映射,就是对未被映射的列进行处理 - 调用
applyAutomaticMappings
方法,从结果集中将未被映射的列值设置到返回结果metaObject
中,返回是否映射成功(设置了1个或以上的属性值) - 调用
applyPropertyMappings
方法,从结果集中将 ResultMap 中需要映射的列值设置到返回结果metaObject
中,返回是否映射成功(设置了1个或以上的属性值) - 如果没有成功映射任意一个属性,则根据
returnInstanceForEmptyRow
全局配置(默认为false)返回空对象还是 null
- 将返回结果的实例对象封装成 MetaObject 对象
-
返回该结果对象
rowValue
我们逐步来看上面的第2
、3.3
、3.4
、3.5
所调用的方法
- 4.2.1createResultObject方法
- 4.2.2shouldApplyAutomaticMappings方法
- 4.2.3applyAutomaticMappings方法
- 4.2.4applyPropertyMappings方法
4.2.1createResultObject方法
createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix)
方法,创建返回结果的实例对象(如果存在嵌套子查询且是延迟加载则为其创建代理对象)
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader,
String columnPrefix) throws SQLException {
// 标记构造方法中是否使用了参数映射
this.useConstructorMappings = false; // reset previous mapping result
// <1> 记录构造方法的入参类型
final List<Class<?>> constructorArgTypes = new ArrayList<>();
// <2> 记录构造方法的参数值
final List<Object> constructorArgs = new ArrayList<>();
// <3> 创建返回结果的实例对象,该步骤的核心!!!
Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
/*
* <4> 如果返回结果的实例对象不为空,且返回结果没有对应的 TypeHandler 类型处理器
* 则遍历所有的映射列,如果存在嵌套子查询并且要求延迟加载,那么为该返回结果的实例对象创建一个动态代理对象(Javassist)
* 这样一来可以后续将需要延迟加载的属性放入 `lazyLoader` 中即可
*
* 为该对象创建对应的代理对象,其中通过 ResultLoaderMap 对延迟加载的方法进行了增强
* 调用 getter 方法时执行查询并从 ResultLoaderMap 中删除,直接调用 setter 方法也会从中删除
*/
if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
// issue gcode #109 && issue #149
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration,
objectFactory, constructorArgTypes, constructorArgs);
break;
}
}
}
// <5> 记录是否使用有参构造方法创建的该返回结果实例对象
this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
return resultObject;
}
-
记录构造方法的入参类型
-
记录构造方法的参数值
-
调用
createResultObject
方法(重载),创建返回结果的实例对象,该步骤的核心!!! -
如果返回结果的实例对象不为空,且返回结果没有对应的 TypeHandler 类型处理器,例如一个实体类,则遍历所有的映射列,如果存在嵌套子查询并且要求延迟加载,那么为该返回结果的实例对象创建一个动态代理对象(Javassist)
这样一来可以后续将需要延迟加载的属性放入
lazyLoader
中即可,在后续会讲到😈 -
记录是否使用有参构造方法创建的该返回结果实例对象,就是使用了映射,后续判断返回空对象还是null需要用到
-
返回实例对象,也可能是它的动态代理对象
这里我们需要来看到第3
步调用的createResultObject
重载方法
- 4.2.1.1createResultObject重载方法
4.2.1.1createResultObject重载方法
createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
方法,找到构造方法,创建一个实例对象
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes,
List<Object> constructorArgs, String columnPrefix) throws SQLException {
// 获取 Java Type
final Class<?> resultType = resultMap.getType();
// 创建对应的 MetaClass 对象,便于操作
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
// 获取 <constructor /> 标签下构造函数的入参信息,可以通过这些入参确认一个构造函数
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
/*
* 创建结果对象,分为下面4种场景:
* 1. 结果集只有一列,且存在对应的 TypeHandler 类型处理器,例如返回 java.lang.String
* 2. <resultMap /> 标签下配置的 <constructor /> 标签下的构造函数参数信息不为空
* 3. 返回类型为接口,或者有默认的构造方法
* 4. 找到合适的构造方法
*/
if (hasTypeHandlerForResultObject(rsw, resultType)) { // 场景1
// 将该列转换成对应 Java Type 的值,然后返回
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
} else if (!constructorMappings.isEmpty()) { // 场景2
// 根据 <constructor /> 标签下的构造方法入参配置,尝试从结果集中获取入参值,并创建返回结果的实例对象
return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) { // 场景3
// 使用默认无参构造方法创建返回结果的实例对象
return objectFactory.create(resultType);
} else if (shouldApplyAutomaticMappings(resultMap, false)) { // 场景4
// 找到合适的构造方法并创建返回结果对象
return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
}
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
创建结果对象,依次分为下面4种场景:
-
结果集只有一列,且存在对应的 TypeHandler 类型处理器,例如返回 java.lang.String
则调用
createPrimitiveResultObject
方法,将该列转换成对应 Java Type 的值,然后返回 -
<resultMap />
标签下配置的<constructor />
标签下的构造函数参数信息不为空则调用
createParameterizedResultObject
方法,根据<constructor /
标签下的构造方法入参配置,尝试从结果集中获取入参值,并创建返回结果的实例对象 -
返回类型为接口,或者有默认的构造方法
则通过实例工厂
objectFactory
,使用默认无参构造方法创建返回结果的实例对象 -
找到合适的构造方法
则调用
createByConstructorSignature
方法,找到合适的构造方法并创建返回结果对象
好的,接下来我们又要看到第1
、2
、4
步调用的三个方法了
- 4.2.1.2createPrimitiveResultObject方法
- 4.2.1.3createParameterizedResultObject方法
- 4.2.1.4createByConstructorSignature方法
4.2.1.2createPrimitiveResultObject方法
createPrimitiveResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix)
方法,创建返回结果实例对象(通常是Java定义的类型,例如java.lang.String)
private Object createPrimitiveResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix)
throws SQLException {
// 获取 Java Type
final Class<?> resultType = resultMap.getType();
final String columnName;
/*
* 获取列名
*/
if (!resultMap.getResultMappings().isEmpty()) { // 配置了 <resultMap />
// 获取 <resultMap /> 标签下的配置信息
final List<ResultMapping> resultMappingList = resultMap.getResultMappings();
// 因为只有一个参数,则直接取第一个
final ResultMapping mapping = resultMappingList.get(0);
// 从配置中获取 column 属性
columnName = prependPrefix(mapping.getColumn(), columnPrefix);
} else {
// 从结果集中获取列名
columnName = rsw.getColumnNames().get(0);
}
// 通过 Java Type 和列名获取对应的 TypeHandler
final TypeHandler<?> typeHandler = rsw.getTypeHandler(resultType, columnName);
// 通过 TypeHandler 将返回结果转换成对应 Java Type 的值
return typeHandler.getResult(rsw.getResultSet(), columnName);
}
- 通过
ResultSetWrapper
根据Java Type和columnName找到对应的TypeHandler
类型处理器 - 通过
TypeHandler
类型处理器,将结果集中的结果转换成对应的 Java 对象
4.2.1.3createParameterizedResultObject方法
createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType, List<ResultMapping> constructorMappings, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
方法
根据 <resultMap />
标签下的 <constructor />
标签配置的参数构建一个实例对象
Object createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType,
List<ResultMapping> constructorMappings, List<Class<?>> constructorArgTypes, List<Object> constructorArgs,
String columnPrefix) {
// 标记是否找到配置的构造函数的所有入参
boolean foundValues = false;
for (ResultMapping constructorMapping : constructorMappings) {
// 获取参数的 Java Type
final Class<?> parameterType = constructorMapping.getJavaType();
// 获取参数对应的 column 列名
final String column = constructorMapping.getColumn();
final Object value;
try {
/*
* 获取该属性值,可能存在以下几种场景:
* 1. 存在嵌套查询
* 2. 存在嵌套 ResultMap
* 3. 直接获取值
*/
if (constructorMapping.getNestedQueryId() != null) { // 场景1
// 通过嵌套查询获取到该属性值
value = getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix);
} else if (constructorMapping.getNestedResultMapId() != null) { // 场景2
// 获取到嵌套的 ResultMap 对象
final ResultMap resultMap = configuration.getResultMap(constructorMapping.getNestedResultMapId());
// 从结果集中获取到嵌套 ResultMap 对应的值
value = getRowValue(rsw, resultMap, getColumnPrefix(columnPrefix, constructorMapping));
} else { // 场景3
final TypeHandler<?> typeHandler = constructorMapping.getTypeHandler();
// 通过 TypeHandler 从结果集中获取该列的值
value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(column, columnPrefix));
}
} catch (ResultMapException | SQLException e) {
throw new ExecutorException("Could not process result for mapping: " + constructorMapping, e);
}
constructorArgTypes.add(parameterType);
constructorArgs.add(value);
foundValues = value != null || foundValues;
}
// 如果构造函数的入参全部找到,则创建返回结果的实例对象
return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
}
-
需要先从结果集中获取每个
<constructor />
标签配置的参数对应的值,这里又可能存在以下三种情况:- 该参数存在嵌套查询,则调用
getNestedQueryConstructorValue
方法,获取到该属性值 - 存在嵌套 ResultMap,则调用
getRowValue
方法,从该结果集中获取到嵌套 ResultMap 对应的值,回到了4.2getRowValue方法 - 正常情况,通过TypeHandler类型处理器,根据列名从结果集中获取到该属性值
- 该参数存在嵌套查询,则调用
-
通过
objectFactory
实例工厂,根据上面配置的入参信息构建一个实例对象
这里我们又要进入第1.1
步的方法
- 4.2.1.3.1getNestedQueryConstructorValue方法
4.2.1.3.1getNestedQueryConstructorValue方法
getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix)
方法,处理构造方法的入参出现嵌套子查询这种情况,获取该参数值
private Object getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix)
throws SQLException {
// <1> 获得嵌套查询关联的 id
final String nestedQueryId = constructorMapping.getNestedQueryId();
// <2> 获取嵌套查询对应的 MappedStatement 对象
final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
// <3> 获取嵌套查询的参数类型
final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
// <4> 获取嵌套查询的参数对象,已完成初始化
final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, constructorMapping, nestedQueryParameterType, columnPrefix);
Object value = null;
// <5> 执行查询
if (nestedQueryParameterObject != null) {
// <5.1> 获取嵌套查询中的 SQL 对象
final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
// <5.2> 获取CacheKey对象
final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
final Class<?> targetType = constructorMapping.getJavaType();
// <5.3> 创建 ResultLoader 对象
final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery,
nestedQueryParameterObject, targetType, key, nestedBoundSql);
// <5.4> 加载结果
value = resultLoader.loadResult();
}
return value;
}
-
获得嵌套查询关联的 id
-
获取嵌套查询对应的 MappedStatement 对象
-
获取嵌套查询的参数类型
-
获取嵌套查询的参数对象,已完成初始化,调用
prepareParameterForNestedQuery
方法,进去后发现又得两层方法🙂,这里就不再展开了,比较简单,可以先参考的我的注释查看,在后续还会调用该方法,再进行解析 -
执行查询,因为这里的构造方法中的入参,所以无需判断延迟加载,在后面设置属性时就不一样了
- 获取嵌套查询中的 SQL 对象
- 获取CacheKey对象
- 创建 ResultLoader 对象
- 加载结果
-
返回子查询返回的值
4.2.1.4createByConstructorSignature方法
createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs)
方法,尝试找一个合适的构造方法构建一个实例对象
private Object createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType,
List<Class<?>> constructorArgTypes, List<Object> constructorArgs) throws SQLException {
// <1> 获取所有的构造函数
final Constructor<?>[] constructors = resultType.getDeclaredConstructors();
// <2> 找到添加了 @AutomapConstructor 注解的构造方法
final Constructor<?> defaultConstructor = findDefaultConstructor(constructors);
if (defaultConstructor != null) {
// 使用这个构造方法创建返回结果的实例对象
return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, defaultConstructor);
} else {
for (Constructor<?> constructor : constructors) { // <3> 遍历所有的构造方法
// 如果构造方法的入参与结果集中列的个数相同,并且入参的 Java Type 和列的 Jdbc Type 有类型处理器
if (allowedConstructorUsingTypeHandlers(constructor, rsw.getJdbcTypes())) {
// 使用这个构造方法创建返回结果的实例对象
return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, constructor);
}
}
}
throw new ExecutorException("No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames());
}
- 获取所有的构造函数
- 找到添加了
@AutomapConstructor
注解的构造方法,如果存在则调用createUsingConstructor
方法,创建一个实例对象 - 否则,遍历所有的构造方法
- 如果构造方法的入参与结果集中列的个数相同,并且入参的 Java Type 和列的 Jdbc Type 有类型处理器
- 使用这个构造方法创建返回结果的实例对象,调用
createUsingConstructor
方法,创建一个实例对象
上面需要调用的createUsingConstructor
方法比较简单,这里就不再展开了,大致逻辑就是从结果集中获取到该构造方法所有的入参,然后构建一个实例对象
4.2.2shouldApplyAutomaticMappings方法
shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested)
方法,检测是否需要自动映射(对未被映射的列进行处理)
private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) {
/*
* 获取<resultMap />中的 autoMapping 配置
* 如果不为空则返回该值,是否自定映射
*/
if (resultMap.getAutoMapping() != null) {
return resultMap.getAutoMapping();
} else {
/*
* 全局配置 AutoMappingBehavior 默认为 PARTIAL
* 如果是嵌套,这里默认就返回 false
*/
if (isNested) { // 嵌套映射
return AutoMappingBehavior.FULL == configuration.getAutoMappingBehavior();
} else {
return AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior();
}
}
}
- 如果
<resultMap />
中的autoMapping
配置不为空,则返回该配置 - 否则通过全局配置来判断,默认
PARTIAL
,也就是不是嵌套映射则需要对未被映射的列进行处理,嵌套查询的话不会对未被映射的列进行处理(需要配置为FULL
)
4.2.3applyAutomaticMappings方法
applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix)
方法,对未被映射的字段进行映射
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject,
String columnPrefix) throws SQLException {
// <1> 将这些未被映射的字段创建对应的 UnMappedColumnAutoMapping 对象
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
// 标记是否找到1个以上的属性值,延迟加载也算
boolean foundValues = false;
if (!autoMapping.isEmpty()) {
// <2> 遍历未被映射的字段数组,将这些属性设置到返回结果对象中
for (UnMappedColumnAutoMapping mapping : autoMapping) {
// <2.1> 通过 TypeHandler 获取未被映射的字段的值
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
if (value != null) {
foundValues = true;
}
/*
* <2.2> 如果属性值不为空,或者配置了值为 null 也往返回结果设置该属性值(不能是基本类型)
*/
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
对象(包含列名、属性名、类型处理器、是否为原始类型) - 遍历未被映射的字段数组,将这些属性设置到返回结果对象中
- 通过 TypeHandler 类型处理器获取未被映射的字段的值
- 如果属性值不为空,或者配置了值为 null 也往返回结果设置该属性值(不能是基本类型),则往返回结果中设置该属性值
这里我们来看到createAutomaticMappings
方法
- 4.2.3.1createAutomaticMappings方法
4.2.3.1createAutomaticMappings方法
createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix)
方法,将这些未被映射的字段创建对应的 UnMappedColumnAutoMapping 对象
private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap,
MetaObject metaObject, String columnPrefix) throws SQLException {
// <1> ResultMap 中需要 "自动映射" 的列会缓存起来,这是对应的缓存 key
final String mapKey = resultMap.getId() + ":" + columnPrefix;
// <2> 先从缓存中获取
List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
if (autoMapping == null) {
autoMapping = new ArrayList<>();
// <3> 获取未映射的的列名集合,也就是数据库返回的列名在 ResultMap 中没有配置,例如我们配置的是 resultType 属性就全部没有配置
final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
for (String columnName : unmappedColumnNames) {
String propertyName = columnName;
/*
* <4> 如果配置了前缀,则将列名中的前缀去掉作为属性名
*/
if (columnPrefix != null && !columnPrefix.isEmpty()) {
// When columnPrefix is specified, ignore columns without the prefix.
if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
// 如果列名以前缀开头则将前缀去除
propertyName = columnName.substring(columnPrefix.length());
} else {
continue;
}
}
/**
* <5> 根据列名从入参对象中获取对应的属性名称,不管大小写都可以找到
* {@link org.apache.ibatis.reflection.Reflector#caseInsensitivePropertyMap)
*/
final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
// <6> 开始创建 UnMappedColumnAutoMapping 对象
if (property != null && metaObject.hasSetter(property)) {
if (resultMap.getMappedProperties().contains(property)) {
// 如果该属性配置了映射关系则跳过
continue;
}
// <6.1> 获取属性名称的 Class 对象
final Class<?> propertyType = metaObject.getSetterType(property);
if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
// <6.2.1> 创建该属性的 UnMappedColumnAutoMapping 对象,设置列名、属性名、类型处理器、是否为原始类型
autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
} else {
// <6.2.2> 执行发现自动映射目标为未知列(或未知属性类型)的行为,默认为 NONE,不做任何行为
configuration.getAutoMappingUnknownColumnBehavior().doAction(mappedStatement, columnName, property, propertyType);
}
} else {
// 执行发现自动映射目标为未知列(或未知属性类型)的行为,默认为 NONE,不做任何行为
configuration.getAutoMappingUnknownColumnBehavior().doAction(mappedStatement, columnName,
(property != null) ? property : propertyName, null);
}
}
autoMappingsCache.put(mapKey, autoMapping);
}
return autoMapping;
}
-
ResultMap 中需要 "自动映射" 的列会缓存起来,这是对应的缓存 key
-
先从
autoMappingsCache
缓存中获取该 ResultMap 对应的UnMappedColumnAutoMapping
集合autoMapping
,没有的话才进行接下来的解析 -
获取未映射的的列名集合,也就是数据库返回的列名在 ResultMap 中没有配置,例如我们配置的是 resultType 属性就全部没有配置,然后进行遍历
-
如果配置了前缀,则将列名中的前缀去掉作为属性名
-
根据列名从入参对象中获取对应的属性名称,不管大小写都可以找到
-
开始为该属性创建
UnMappedColumnAutoMapping
对象,如果返回对象中有该属性的 setter 方法- 获取属性名称的 Class 对象
- 如果有对应的 TypeHandler 类型处理器,创建该属性的
UnMappedColumnAutoMapping
对象,设置列名、属性名、类型处理器、是否为原始类型,添加到autoMapping
集合中 - 否则,执行发现自动映射目标为未知列(或未知属性类型)的行为,默认为
NONE
,不做任何行为
-
该属性没有setter方法,执行发现自动映射目标为未知列(或未知属性类型)的行为,默认为
NONE
,不做任何行为 -
返回
autoMapping
,并添加到autoMappingsCache
缓存中
4.2.4applyPropertyMappings方法
applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
方法,将明确被映射的字段设置到返回结果中
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject,
ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
// <1> 获取 ResultMap 中明确需要进行映射的列名集合
final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
// 标记是否找到1个以上的属性值,延迟加载也算
boolean foundValues = false;
// <2> 获取 ResultMap 中所有的 ResultMapping 对象
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;
}
/*
* <3> 从结果集获取属性值设置到返回结果中,处理以下三种场景:
* 1. 配置的 column 属性为"{prop1:col1,prop2:col2}"这种形式,
* 一般就是嵌套子查询,表示将col1和col2的列值设置到嵌套子查询的入参对象的prop1和prop2属性中
* 2. 基本类型的属性映射
* 3. 多结果集的场景处理,该属性来自另一个结果集
*
* 对于没有配置 column 属性不会处理
*/
if (propertyMapping.isCompositeResult() // 场景1
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) // 场景2
|| propertyMapping.getResultSet() != null) { // 场景3
// <4> 完成映射,从结果集中获取到对应的属性值
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
// issue #541 make property optional
final String property = propertyMapping.getProperty();
if (property == null) {
// <4.1> 没有配置对应的 Java 属性则跳过
continue;
} else if (value == DEFERRED) {
// <4.2> 如果是占位符,则跳过
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')
// <4.3> 将属性值设置到返回结果中
metaObject.setValue(property, value); // 设置属性值
}
}
}
return foundValues;
}
-
获取 ResultMap 中明确需要进行映射的列名集合
mappedColumnNames
-
获取 ResultMap 中所有的 ResultMapping 对象,然后进行遍历
-
从结果集获取属性值设置到返回结果中,需要满足下面三个条件中的一个:
- 配置的
column
属性为{prop1:col1,prop2:col2}
这种形式,一般就是嵌套子查询,表示将col1和col2的列值设置到嵌套子查询的入参对象的prop1和prop2属性中 - 基本类型的属性映射
- 多结果集的场景处理,该属性来自另一个结果集,存储过程相关,本文暂不分析
- 配置的
-
完成映射,调用
getPropertyMappingValue
方法,从结果集中获取到对应的属性值value
- 没有配置对应的 Java 属性则跳过
- 如果是
DEFERRED
占位符(延迟加载),则跳过 - 将属性值设置到返回结果中
我们来看看getPropertyMappingValue
方法
- 4.2.4.1getPropertyMappingValue方法
4.2.4.1getPropertyMappingValue方法
getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
方法
完成映射,从结果集中获取到对应的属性值
private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping,
ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
if (propertyMapping.getNestedQueryId() != null) { // 嵌套子查询
// <1> 执行嵌套子查询,返回查询结果,如果需要延迟记载则返回的是 DEFERRED
return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
} else if (propertyMapping.getResultSet() != null) { // 多结果集,存储过程相关,暂时忽略
// <2> 多结果集处理,延迟加载,返回占位符
addPendingChildRelation(rs, metaResultObject, propertyMapping);
return DEFERRED;
} else { // 结果映射
// 获取 TypeHandler 类型处理器
final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
// <3> 通过 TypeHandler 类型处理器从结果集中获取该列对应的属性值
return typeHandler.getResult(rs, column);
}
}
可以看到该ResultMapping
属性配置可能有三种情况:
- 如果是嵌套子查询,则调用
getNestedQueryMappingValue
方法,执行嵌套子查询,返回查询结果,如果需要延迟记载则返回的是DEFERRED
- 如果是多结果集,存储过程相关,则调用
addPendingChildRelation
方法, 多结果集处理,延迟加载,返回占位符,本文暂不分析 - 正常情况,获取 TypeHandler 类型处理器,通过它从结果集中获取该列对应的属性值
我们这里继续看到第1
步中的调用的方法
- 4.2.4.2getNestedQueryMappingValue方法
4.2.4.2getNestedQueryMappingValue方法
getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
方法
执行嵌套子查询,返回查询结果,如果需要延迟记载则返回的是 DEFERRED
private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping,
ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
// <1> 获取嵌套子查询关联的ID
final String nestedQueryId = propertyMapping.getNestedQueryId();
// 获得属性名
final String property = propertyMapping.getProperty();
// 获得嵌套子查询的 MappedStatement 对象
final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
// 获得嵌套子查询的参数类型
final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
// <2> 准备好嵌套子查询的入参
final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, propertyMapping,
nestedQueryParameterType, columnPrefix);
Object value = null;
if (nestedQueryParameterObject != null) {
// <3> 获得嵌套子查询的 BoundSql 对象
final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
// <4> 获得嵌套子查询本次查询的 CacheKey 对象
final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
// 嵌套子查询的返回 Java Type
final Class<?> targetType = propertyMapping.getJavaType();
// <5> 检查缓存中已存在
if (executor.isCached(nestedQuery, key)) {
// <5.1> 创建 DeferredLoad 对象,并通过该 DeferredLoad 对象从缓存中加载结果对象
executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
// <5.2> 返回已定义
value = DEFERRED;
} else { // <6> 缓存中不存在
// <6.1> 创建 ResultLoader 对象
final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery,
nestedQueryParameterObject, targetType, key, nestedBoundSql);
if (propertyMapping.isLazy()) { // <6.2> 如果要求延迟加载,则延迟加载
// <6.2.1> 如果该属性配置了延迟加载,则将其添加到 `ResultLoader.loaderMap` 中,等待真正使用时再执行嵌套查询并得到结果对象
lazyLoader.addLoader(property, metaResultObject, resultLoader);
// <6.2.2> 返回延迟加载占位符
value = DEFERRED;
} else { // <6.3> 如果不要求延迟加载,则直接执行加载对应的值
value = resultLoader.loadResult();
}
}
}
return value;
}
-
获取嵌套子查询关联的ID、属性名、嵌套子查询的
MappedStatement
对象、嵌套子查询的参数类型 -
从结果集中获取参数值,准备好嵌套子查询的入参,调用
prepareParameterForNestedQuery
方法 -
获得嵌套子查询的 BoundSql 对象
-
获得嵌套子查询本次查询的 CacheKey 对象、嵌套子查询的返回 Java Type
-
检查缓存中已存在本次子查询的数据,已存在的话则进行下面两步
-
则创建
DeferredLoad
对象,并通过该DeferredLoad
对象从缓存中加载结果对象这也算延迟加载,嵌套子查询的结果在缓存中,然后会在查询接口后进行加载,可以回到《SQL执行过程(一)之Executor》的BaseExecutor小节中的
query
方法的第6
步看看 -
返回
DEFERRED
延迟加载默认对象
-
-
缓存中不存在本次子查询的数据
-
创建
ResultLoader
对象resultLoader
-
如何该属性还要求了是延迟加载
-
则将其添加到
ResultLoader.loaderMap
中,等待真正使用时再执行嵌套查询并得到结果对象可以回到4.2.1createResultObject方法的第
4
步看一下,如果存在嵌套子查询并且要求延迟加载,那么为该返回结果的实例对象创建一个动态代理对象(Javassist),后续将需要延迟加载的属性放入lazyLoader
(就是上面的ResultLoader)中即可 -
返回
DEFERRED
延迟加载默认对象
-
-
否在直接加载
resultLoader
对象,获取到该属性值
-
-
最后返回该属性值或者
DEFERRED
延迟加载默认对象
这里我们再来看到第2
步中调用的方法
- 4.2.4.2.1prepareParameterForNestedQuery方法
4.2.4.2.1prepareParameterForNestedQuery方法
prepareParameterForNestedQuery(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix)
方法,为嵌套子查询准备好入参
private Object prepareParameterForNestedQuery(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType,
String columnPrefix) throws SQLException {
if (resultMapping.isCompositeResult()) { // 嵌套子查询是否有多个属性映射
// 从结果集中获取多个属性值设置到入参对象中
return prepareCompositeKeyParameter(rs, resultMapping, parameterType, columnPrefix);
} else {
// 从结果集中直接获取嵌套查询的入参
return prepareSimpleKeyParameter(rs, resultMapping, parameterType, columnPrefix);
}
}
-
嵌套子查询是有多个属性映射,则调用
prepareCompositeKeyParameter
方法,从结果集中获取多个属性值设置到入参对象中配置的
column
属性为{prop1:col1,prop2:col2}
这种形式,表示将col1和col2的列值设置到嵌套子查询的入参对象的prop1和prop2属性中 -
只有一个属性映射,则调用
prepareSimpleKeyParameter
方法,从结果集中直接获取嵌套查询的入参
来看看第1
、2
步调用的方法
- 4.2.4.2.2prepareCompositeKeyParameter方法
- 4.2.4.2.3prepareSimpleKeyParameter方法
4.2.4.2.2prepareCompositeKeyParameter方法
prepareCompositeKeyParameter(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix)
方法
处理嵌套子查询有多个属性映射作为入参的场景,获取到多个属性值到子查询的入参中
private Object prepareCompositeKeyParameter(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType,
String columnPrefix) throws SQLException {
// 创建一个嵌套子查询的入参的实例对象
final Object parameterObject = instantiateParameterObject(parameterType);
final MetaObject metaObject = configuration.newMetaObject(parameterObject);
// 标记是否找到一个或以上的属性值
boolean foundValues = false;
for (ResultMapping innerResultMapping : resultMapping.getComposites()) {
// 获取嵌套子查询的入参该属性的 Java Type
final Class<?> propType = metaObject.getSetterType(innerResultMapping.getProperty());
final TypeHandler<?> typeHandler = typeHandlerRegistry.getTypeHandler(propType);
//通过 TypeHandler 根据该属性的 column 列名从该结果集中获取值
final Object propValue = typeHandler.getResult(rs, prependPrefix(innerResultMapping.getColumn(), columnPrefix));
// issue #353 & #560 do not execute nested query if key is null
if (propValue != null) {
// 设置属性值到入参对象中
metaObject.setValue(innerResultMapping.getProperty(), propValue);
foundValues = true;
}
}
return foundValues ? parameterObject : null;
}
-
创建一个嵌套子查询的入参的实例对象
parameterObject
,调用instantiateParameterObject
方法,很简单,点击去看一下就知道了 -
开始遍历ResultMapping中的
List<ResultMapping> composites
组合字段 -
获取嵌套子查询的入参该属性对应的 TypeHandler 处理器
-
通过 TypeHandler 根据该属性的
column
列名从该结果集中获取值 -
设置属性值到子查询的入参对象中
-
返回
parameterObject
子查询入参对象
4.2.4.2.3prepareSimpleKeyParameter方法
prepareSimpleKeyParameter(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix)
方法,从结果集中直接获取嵌套查询的入参
private Object prepareSimpleKeyParameter(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType,
String columnPrefix) throws SQLException {
final TypeHandler<?> typeHandler;
if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
typeHandler = typeHandlerRegistry.getTypeHandler(parameterType);
} else {
typeHandler = typeHandlerRegistry.getUnknownTypeHandler();
}
// 根据类型处理器从结果集中获取该列的值,作为嵌套查询的入参
return typeHandler.getResult(rs, prependPrefix(resultMapping.getColumn(), columnPrefix));
}
- 根据Java Type获取到 TypeHandler 类型处理器
- 根据TypeHandler从结果集中将该列对应的值转化成入参
4.3storeObject方法
storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs)
方法
将返回结果 对象保存至 resultHandler
,或者设置到父对象 parentMapping
(存储过程相关,本文暂不分析)的对应属性中
private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext,
Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
if (parentMapping != null) {
// 嵌套查询或嵌套映射,将返回结果设置到父对象的对应属性中
linkToParents(rs, parentMapping, rowValue);
} else {
// 普通映射,将结果对象保存到 ResultHandler 中
callResultHandler(resultHandler, resultContext, rowValue);
}
}
- 如果
parentMapping
不为空,则调用linkToParents
方法,嵌套查询或嵌套映射,将返回结果设置到父对象的对应属性中,存储过程相关,本文暂不分析 - 调用
callResultHandler
方法,普通映射,将结果对象保存到 ResultHandler 中
来看到第2
步调用的方法
4.3.1callResultHandler方法
callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue)
方法
普通映射,将结果对象保存到 ResultHandler 中
private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
/*
* 递增返回结果数量 resultCount
* 保存返回结果 resultObject
*/
resultContext.nextResultObject(rowValue);
// 将返回结果保存至 ResultHandler 中
((ResultHandler<Object>) resultHandler).handleResult(resultContext);
}
-
在
resultContext
上下文对象对象中暂存返回结果rowValue
,并递增返回结果的数量,可以回到4.handleRowValuesForSimpleResultMap方法看一下 -
通过
resultHandler
对resultContext
上下文对象暂存的返回结果进行处理,在DefaultResultHandler
中你可以看到public class DefaultResultHandler implements ResultHandler<Object> { private final List<Object> list; public DefaultResultHandler() { list = new ArrayList<>(); } @Override public void handleResult(ResultContext<?> context) { list.add(context.getResultObject()); } }
就是往List集合中添加返回结果
结束语
回顾到3.handleRowValues方法中,上面已经对结果集(不含嵌套ResultMap)进行映射的整个过程进行了分析,在defaultResultHandler
和multipleResults
都可以获取到映射后的结果
本来想继续分析结果集(含嵌套ResultMap)这种情况的,调用的是handleRowValuesForNestedResultMap
方法,由于上面已经嵌套太多层方法了,就不再分析第二种更复杂的情况,本文本身就不好编排,再进行嵌套就无法阅读了😢,可以参考DefaultResultSetHandler.java根据注释,进行理解
其中涉及到的DefaultResultContext
和DefaultResultHandler
都比较简单,也不列出来了,自行根据注释查看一下
总结
本文分析了DefaultResultSetHandler
是如何处理结果集,进行映射,转换成Java对象的,总的来说就是根据ResultMap对象,对于不同的场景进行处理分析,映射成我们需要的Java对象,需要考虑的情况太多,所以这个类比较复杂,这里也仅对结果集(不含嵌套ResultMap)进行映射的整个过程进行了分析,关于含嵌套ResultMap的结果集来说,可能更加稍微复杂一点,不过也相差无几,可以参考DefaultResultSetHandler.java
到这里SQL执行过程算是结束了,但是其中还有一部分延迟加载的内容没有分析,本来准备在这篇文档中分析的,发现已经写太多内容了,所以放在下一篇文档中
参考文章:芋道源码《精尽 MyBatis 源码分析》