MyBatis select 语句的执行 - litter-fish/ReadSource GitHub Wiki
SELECT 执行时序图:
// org/apache/ibatis/binding/MapperMethod.java
public Object execute(SqlSession sqlSession, Object[] args) {
Object result; // 返回结果
switch (command.getType()) {
// 省略其他逻辑
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
/*
* 如果方法返回值为 void,但参数列表中包含 ResultHandler,表明使用者
* 想通过 ResultHandler 的方式获取查询结果,而非通过返回值获取结果
*/
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
// 执行查询操作,并返回多个结果
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
// 执行查询操作,并将结果封装在 Map 中返回
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
// 执行查询操作,并返回一个 Cursor 对象
result = executeForCursor(sqlSession, args);
} else {
// 参数转换
Object param = method.convertArgsToSqlCommandParam(args);
// 执行查询操作,并返回一个结果
result = sqlSession.selectOne(command.getName(), param);
}
break;
// 省略其他逻辑
}
// 如果方法的返回值为基本类型,而返回值却为 null,此种情况下应抛出异常
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("......");
}
return result;
}
转换参数到sql命令的参数
// org/apache/ibatis/binding/MapperMethod.java
public Object convertArgsToSqlCommandParam(Object[] args) {
return paramNameResolver.getNamedParams(args);
}
// org/apache/ibatis/reflection/ParamNameResolver.java
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
if (args == null || paramCount == 0) {
// 如果没参数
return null;
} else if (!hasParamAnnotation && paramCount == 1) {
/*
* 如果方法参数列表无 @Param 注解,且仅有一个非特别参数,则返回该参数的值。
* 比如如下方法:
* List findList(RowBounds rb, String name)
* names 如下:
* names = {1 : "0"}
* 此种情况下,返回 args[names.firstKey()],即 args[1] -> name
*/
return args[names.firstKey()];
} else {
// 否则,返回一个ParamMap,修改参数名,参数名就是其位置
final Map<String, Object> param = new ParamMap<Object>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
// 添加 <参数名, 参数值> 键值对到 param 中
param.put(entry.getValue(), args[entry.getKey()]);
// genericParamName = param + index。比如 param1, param2, ... paramN
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
/*
* 检测 names 中是否包含 genericParamName,什么情况下会包含?答案如下:
*
* 使用者显式将参数名称配置为 param1,即 @Param("param1")
*/
if (!names.containsValue(genericParamName)) {
// 添加 <param*, value> 到 param 中
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
返回一个结果的 selectOne 方法分析
// org/apache/ibatis/session/defaults/DefaultSqlSession.java
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
// 调用 selectList 获取结果
List<T> list = this.<T>selectList(statement, parameter);
if (list.size() == 1) {
// 返回结果
return list.get(0);
} else if (list.size() > 1) {
// 如果查询结果大于1则抛出异常,这个异常也是很常见的
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
返回多个结果的执行方法 executeForMany 分析
// org/apache/ibatis/binding/MapperMethod.java
// 返回多行结果 调用sqlSession.selectList方法
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
// 如果参数含有rowBounds则调用分页的查询
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
// 没有分页则调用普通查询
result = sqlSession.<E>selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
调用 selectList 获取结果
// org/apache/ibatis/session/defaults/DefaultSqlSession.java
public <E> List<E> selectList(String statement, Object parameter) {
// 调用重载方法
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 获取 MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
// 调用 Executor 实现类中的 query 方法
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
装饰类 CachingExecutor 的 query 查询处理
// org/apache/ibatis/executor/CachingExecutor.java
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 获取 BoundSql
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 创建 CacheKey
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
// 调用重载方法
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
// org/apache/ibatis/mapping/MappedStatement.java
public BoundSql getBoundSql(Object parameterObject) {
// 调用 sqlSource 的 getBoundSql 获取 BoundSql
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.isEmpty()) {
/*
* 创建新的 BoundSql,这里的 parameterMap 是 ParameterMap 类型。
* 由<ParameterMap> 节点进行配置,该节点已经废弃,不推荐使用。默认情况下,
* parameterMap.getParameterMappings() 返回空集合
*/
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
}
// check for nested result maps in parameter mappings (issue #30)
// 检查参数映射中的嵌套结果映射
for (ParameterMapping pm : boundSql.getParameterMappings()) {
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = configuration.getResultMap(rmId);
if (rm != null) {
hasNestedResultMaps |= rm.hasNestedResultMaps();
}
}
}
return boundSql;
}
sqlSource在解析mapper文件时会根据是否时动态标签,设置不同对象类型:DynamicSqlSource、RawSqlSource。
调用 sqlSource 的 getBoundSql 获取 BoundSql
// org/apache/ibatis/scripting/xmltags/DynamicSqlSource.java
public BoundSql getBoundSql(Object parameterObject) {
// 创建 DynamicContext
DynamicContext context = new DynamicContext(configuration, parameterObject);
// rootSqlNode在解析mapper配置文件时设置为MixedSqlNode,解析 SQL 片段,并将解析结果存储到 DynamicContext 中
rootSqlNode.apply(context);
// 创建 SqlSourceBuilder 对象
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
/*
* 构建 StaticSqlSource,在此过程中将 sql 语句中的占位符 #{} 替换为问号 ?,
* 并为每个占位符构建相应的 ParameterMapping
*/
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
// 调用 StaticSqlSource 的 getBoundSql 获取 BoundSql
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
// 将 DynamicContext 的 ContextMap 中的内容拷贝到 BoundSql 中
for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
}
return boundSql;
}
处理逻辑:
- 首先创建 DynamicContext,
- 其次解析 SQL 片段,并将解析结果存储到 DynamicContext 中,
- 接着解析 SQL 语句,并构建 StaticSqlSource,
- 然后调用 StaticSqlSource 的 getBoundSql 获取 BoundSql,
- 最后将 DynamicContext 的 ContextMap 中的内容拷贝到 BoundSql 中。
创建 DynamicContext
// org/apache/ibatis/scripting/xmltags/DynamicContext.java
public DynamicContext(Configuration configuration, Object parameterObject) {
// 创建 ContextMap
if (parameterObject != null && !(parameterObject instanceof Map)) {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
bindings = new ContextMap(metaObject);
} else {
bindings = new ContextMap(null);
}
// 存放运行时参数 parameterObject 以及 databaseId
bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());
}
解析 SQL 片段
// org/apache/ibatis/scripting/xmltags/MixedSqlNode.java
public boolean apply(DynamicContext context) {
// 遍历 SqlNode 集合
/**
* StaticTextSqlNode 用于存储静态文本
* TextSqlNode 用于存储带有 ${} 占位符的文本,
* IfSqlNode 则用于存储 <if> 节点的内容
*/
for (SqlNode sqlNode : contents) {
// 调用 sqlNode 对象本身的 apply 方法解析 sql
sqlNode.apply(context);
}
return true;
}
文本节点的解析
// org/apache/ibatis/scripting/xmltags/TextSqlNode.java
public boolean apply(DynamicContext context) {
// 创建 ${} 占位符解析器
GenericTokenParser parser = createParser(new BindingTokenParser(context, injectionFilter));
// 解析 ${} 占位符,并将解析结果添加到 DynamicContext 中
context.appendSql(parser.parse(text));
return true;
}
IF节点的解析
// org/apache/ibatis/scripting/xmltags/IfSqlNode.java
public boolean apply(DynamicContext context) {
// 通过 ONGL 评估 test 表达式的结果
if (evaluator.evaluateBoolean(test, context.getBindings())) {
// 若 test 表达式中的条件成立,则调用其他节点的 apply 方法进行解析
// contents 代表 MixedSqlNode 实例
contents.apply(context);
return true;
}
return false;
}
trim节点的解析
// org/apache/ibatis/scripting/xmltags/TrimSqlNode.java
public boolean apply(DynamicContext context) {
// 创建具有过滤功能的 DynamicContext
FilteredDynamicContext filteredDynamicContext = new FilteredDynamicContext(context);
// 解析节点内容
boolean result = contents.apply(filteredDynamicContext);
// 过滤掉前缀和后缀
filteredDynamicContext.applyAll();
return result;
}
// org/apache/ibatis/scripting/xmltags/TrimSqlNode.FilteredDynamicContext
public void applyAll() {
sqlBuffer = new StringBuilder(sqlBuffer.toString().trim());
String trimmedUppercaseSql = sqlBuffer.toString().toUpperCase(Locale.ENGLISH);
if (trimmedUppercaseSql.length() > 0) {
// 引用前缀和后缀,也就是对 sql 进行过滤操作,移除掉前缀或后缀
applyPrefix(sqlBuffer, trimmedUppercaseSql);
applySuffix(sqlBuffer, trimmedUppercaseSql);
}
// 将当前对象的 sqlBuffer 内容添加到代理类中
delegate.appendSql(sqlBuffer.toString());
}
// org/apache/ibatis/scripting/xmltags/TrimSqlNode.FilteredDynamicContext
private void applyPrefix(StringBuilder sql, String trimmedUppercaseSql) {
if (!prefixApplied) {
// 设置 prefixApplied 为 true,以下逻辑仅会被执行一次
prefixApplied = true;
if (prefixesToOverride != null) {
for (String toRemove : prefixesToOverride) {
// 检测当前 sql 字符串是否包含 toRemove 前缀,比如 'AND ', 'AND\t'
if (trimmedUppercaseSql.startsWith(toRemove)) {
// 移除前缀
sql.delete(0, toRemove.trim().length());
break;
}
}
}
if (prefix != null) {
// 插入前缀,比如 WHERE
sql.insert(0, " ");
sql.insert(0, prefix);
}
}
}
// org/apache/ibatis/scripting/xmltags/TrimSqlNode.FilteredDynamicContext
private void applySuffix(StringBuilder sql, String trimmedUppercaseSql) {
if (!suffixApplied) {
suffixApplied = true;
if (suffixesToOverride != null) {
for (String toRemove : suffixesToOverride) {
if (trimmedUppercaseSql.endsWith(toRemove) || trimmedUppercaseSql.endsWith(toRemove.trim())) {
int start = sql.length() - toRemove.trim().length();
int end = sql.length();
sql.delete(start, end);
break;
}
}
}
if (suffix != null) {
sql.append(" ");
sql.append(suffix);
}
}
}
Choose节点解析
// org/apache/ibatis/scripting/xmltags/ChooseSqlNode.java
public boolean apply(DynamicContext context) {
// 遍历所有when分支节点,只要遇到第一个为true就返回
for (SqlNode sqlNode : ifSqlNodes) {
if (sqlNode.apply(context)) {
return true;
}
}
// 全部when都为false时,走otherwise分支
if (defaultSqlNode != null) {
defaultSqlNode.apply(context);
return true;
}
return false;
}
ForEach节点解析
构建 StaticSqlSource,并将 sql 语句中的占位符 #{} 替换为问号 ?
// org/apache/ibatis/builder/SqlSourceBuilder.java
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
// 创建 #{} 占位符处理器
ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
// 创建 #{} 占位符解析器
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
// 解析 #{} 占位符,并返回解析结果
String sql = parser.parse(originalSql);
// 封装解析结果到 StaticSqlSource 中,并返回
return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}
// org/apache/ibatis/executor/CachingExecutor.java
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);
}
// org/apache/ibatis/executor/BaseExecutor.java
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
// 创建 CacheKey 对象
CacheKey cacheKey = new CacheKey();
// 将 MappedStatement 的 id 作为影响因子进行计算
cacheKey.update(ms.getId());
// RowBounds 用于分页查询,下面将它的两个字段作为影响因子进行计算
cacheKey.update(rowBounds.getOffset());
cacheKey.update(rowBounds.getLimit());
// 获取 sql 语句,并进行计算
cacheKey.update(boundSql.getSql());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
// mimic DefaultParameterHandler logic
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
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);
}
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// issue #176
// 获取 Environment id 遍历,并让其参与计算
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
CacheKey 对象重要属性
private static final int DEFAULT_MULTIPLYER = 37;
private static final int DEFAULT_HASHCODE = 17;
// 乘子,默认为37
private final int multiplier;
// CacheKey 的 hashCode,综合了各种影响因子
private int hashcode;
// 校验和
private long checksum;
// 影响因子个数
private int count;
// 影响因子个数
private List<Object> updateList;
更新缓存key
// org/apache/ibatis/cache/CacheKey.java
public void update(Object object) {
int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
// 自增 count
count++;
// 计算校验和
checksum += baseHashCode;
// 更新 baseHashCode
baseHashCode *= count;
// 计算 hashCode
hashcode = multiplier * hashcode + baseHashCode;
// 保存影响因子
updateList.add(object);
}
public <E> List<E> query(MappedStatement ms, Object parameterObject,
RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
// 从 MappedStatement 中获取 Cache,注意这里的 Cache 并非是在 CachingExecutor 中创建的
Cache cache = ms.getCache();
// 若映射文件中未配置缓存或参照缓存,此时 cache = null
if (cache != null) {
// 刷新缓存如果需要
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
// 如果二级缓存中找到了记录就直接返回,否则到DB查询后进行缓存
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
// 若缓存未命中,则调用被装饰类的 query 方法
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
// 调用被装饰类的 query 方法
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
// 如果需要刷新缓存(默认DML需要刷新,也可以语句层面修改), 且queryStack(应该是用于嵌套查询的场景)=0
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
// 从一级缓存中获取缓存项
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
// 只处理存储过程和函数调用的出参, 因为存储过程和函数的返回不是通过ResultMap而是ParameterMap来的,所以只要把缓存的非IN模式参数取出来设置到parameter对应的属性上即可
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 一级缓存未命中,则从数据库中查询
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
// 从一级缓存中延迟加载嵌套查询结果
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
首先尝试从一级缓存中获取,如果未命中缓存则从数据库中查询。
数据库查询逻辑
// org/apache/ibatis/executor/BaseExecutor.java
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
List<E> list;
// 向缓存中存储一个占位符
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
// 调用 doQuery 进行查询
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
// 移除占位符
localCache.removeObject(key);
}
// 缓存查询结果
localCache.putObject(key, list);
// 如果是存储过程类型,则把查询参数放到本地出参缓存中, 所以第一次一定为空
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
doQuery
// org/apache/ibatis/executor/SimpleExecutor.java
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 创建 StatementHandler
// 主要有三种类型的语句处理器UNPREPARE、PREPARE、CALLABLE。
// 默认是PREPARE类型,通过mapper语句上的statementType属性进行设置,一般除了存储过程外不应该设置
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 创建 Statement
stmt = prepareStatement(handler, ms.getStatementLog());
// 执行查询操作
return handler.<E>query(stmt, resultHandler);
} finally {
// 关闭 Statement
closeStatement(stmt);
}
}
首先创建 StatementHandler, 接着创建 Statement, 最后执行查询操作。
执行查询操作
// org/apache/ibatis/executor/statement/SimpleStatementHandler.java
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
// MyBatis 结果集处理
return resultSetHandler.<E>handleResultSets(statement);
}
结果集的时序图
// org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
// 获取第一个结果集
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
// 绝大部分情况下一个查询只有一个ResultMap, 除非多结果集查询
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
// 至少执行一次
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) {
// 如果一个映射语句的resultSet数量比jdbc resultset多的话,多的部分就是嵌套结果集
while (rsw != null && resultSetCount < resultSets.length) {
// nextResultMaps初始化的时候为空,它是在处理主查询每行记录的时候写进去的,所以此时就可以得到主记录是哪个属性
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
// 得到子结果集的resultMap之后,就可以进行填充了
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
首先获取第一个结果集,并封装成 ResultSetWrapper 对象
// org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
// 获取结果集
ResultSet rs = stmt.getResultSet();
while (rs == null) {
// move forward to get the first resultset in case the driver
// doesn't return the resultset as the first result (HSQLDB 2.1)
/*
* 移动 ResultSet 指针到下一个上,有些数据库驱动可能需要使用者
* 先调用 getMoreResults 方法,然后才能调用 getResultSet 方法
* 获取到第一个 ResultSet
*/
if (stmt.getMoreResults()) {
rs = stmt.getResultSet();
} else {
if (stmt.getUpdateCount() == -1) {
// no more results. Must be no resultset
break;
}
}
}
/*
* 这里并不直接返回 ResultSet,而是将其封装到 ResultSetWrapper 中。
* ResultSetWrapper 中包含了 ResultSet 一些元信息,比如列名称、每列对应的 JdbcType、
* 以及每列对应的 Java 类名(class name,譬如 java.lang.String)等。
*/
return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}
处理结果集
// org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults,
ResultMapping parentMapping) throws SQLException {
try {
// 处理嵌套/关联的resultMap(collection或association)
if (parentMapping != null) {
// 处理非主记录 resultHandler传递null,RowBounds传递默认值,parentMapping不为空
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
/*
* 检测 resultHandler 是否为空。ResultHandler 是一个接口,使用者可实现该接口,
* 这样我们可以通过 ResultHandler 自定义接收查询结果的动作。比如我们可将结果存储到
* List、Map 亦或是 Set,甚至丢弃,这完全取决于大家的实现逻辑。
*/
if (resultHandler == null) {
// 创建默认的结果处理器
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
// 处理主记录,resultHander不为空,rowBounds不使用默认值,parentMapping传递null
// 处理结果集的行数据
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
//TODO /////////
// ?????? 作用
multipleResults.add(defaultResultHandler.getResultList());
} else {
// 处理结果集的行数据
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}
处理结果集的行数据
// org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java
// 处理每一行记录,不管是主查询记录还是关联嵌套的查询结果集,他们的入参上通过resultHandler,rowBounds,parentMapping区分
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler,
RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
// 具体实现上,处理嵌套记录和主记录的逻辑不一样
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
// 处理嵌套映射 含有嵌套resultMap的列处理,通常是collection或者association
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
// 处理简单映射
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
处理简单映射时序图:
处理简单映射
// org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java
// 处理主记录
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler,
RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
// 根据 RowBounds 定位到指定行记录,这是mybatis进行内部分页,不是依赖服务器端的分页实现
skipRows(rsw.getResultSet(), rowBounds);
// 检测是否还有更多行的数据需要处理
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
// 解析鉴别器,得到嵌套的最深的鉴别器对应的ResultMap,如果没有鉴别器,就返回最顶层的ResultMap
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
// 这个时候resultMap是非常干净的,没有嵌套任何其他东西了,但是这也是最关键的地方,将ResultSet记录转换为业务层配置的对象类型或者Map类型
Object rowValue = getRowValue(rsw, discriminatedResultMap);
// 存储结果
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
}
默认分页实现,根据 RowBounds 定位到指定行记录
// org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java
private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
// 检测 rs 的类型,不同的类型行数据定位方式是不同的
if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
// 直接定位到 rowBounds.getOffset() 位置处
rs.absolute(rowBounds.getOffset());
}
} else {
for (int i = 0; i < rowBounds.getOffset(); i++) {
/*
* 通过多次调用 rs.next() 方法实现行数据定位。
* 当 Offset 数值很大时,这种效率很低下
*/
rs.next();
}
}
}
鉴别器ResultMap:
解析鉴别器,得到嵌套的最深的鉴别器对应的ResultMap,如果没有鉴别器,就返回最顶层的ResultMap
ResultMap结构
将ResultSet记录转换为业务层配置的对象类型或者Map类型
// org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
// 创建实体类对象,比如 Article 对象
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
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, null) || foundValues;
}
// 处理属性映射,这里会识别出哪些属性需要nestQuery,哪些是nest ResultMap
// 根据 <resultMap> 节点中配置的映射关系进行映射
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
创建实体类对象
// org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader,
String columnPrefix) throws SQLException {
this.useConstructorMappings = false; // reset previous mapping result
final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
final List<Object> constructorArgs = new ArrayList<Object>();
// 调用重载方法创建实体类对象
Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
// 检测实体类是否有相应的类型处理器
if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
// 如果开启了延迟加载,则为 resultObject 生成代理类
// issue gcode #109 && issue #149
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
/*
* 创建代理类,默认使用 Javassist 框架生成代理类。由于实体类通常不会实现接口,
* 所以不能使用 JDK 动态代理 API 为实体类生成代理。
*/
resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration,
objectFactory, constructorArgTypes, constructorArgs);
break;
}
}
}
this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
return resultObject;
}
调用重载方法创建实体类对象
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes,
List<Object> constructorArgs, String columnPrefix)
throws SQLException {
final Class<?> resultType = resultMap.getType();
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
// 获取 <constructor> 节点对应的 ResultMapping
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
// 是否有目标对象类型的处理器,有的话,直接调用类型处理器(这里为什么一定是原生类型)创建目标对象
/*
* 检测是否有与返回值类型相对应的 TypeHandler,若有则直接
* 通过 TypeHandler 从结果集中提取数据,并生成返回值对象
*/
if (hasTypeHandlerForResultObject(rsw, resultType)) {
// 通过 TypeHandler 获取提取,并生成返回值对象
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
// 是否有构造器,有的话,使用指定的构造器创建目标对象(构造器里面如果嵌套了查询或者ResultMap,则进行处理)
} else if (!constructorMappings.isEmpty()) {
/*
* 通过 <constructor> 节点配置的映射信息从 ResultSet 中提取数据,
* 然后将这些数据传给指定构造方法,即可创建实体类对象
*/
return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
// 如果结果类型是接口或者具有默认构造器,则使用ObjectFactory创建默认目标对象
} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
// 通过 ObjectFactory 调用目标类的默认构造方法创建实例
return objectFactory.create(resultType);
// 是否可以应用自动映射,默认是对非嵌套查询,只要没有明确设置AutoMappingBehavior.NONE就可以,对于嵌套查询,AutoMappingBehavior.FULL就可以。
// 自动映射的逻辑是先找目标对象上具有@AutomapConstructor注解的构造器,然后根据ResultSet返回的字段清单找匹配的构造器,如果找不到,就报错
} else if (shouldApplyAutomaticMappings(resultMap, false)) {
// 通过自动映射查找合适的构造方法创建实例
return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
}
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
首先判断是否有目标对象类型的处理器,有的话,直接调用类型处理器(这里为什么一定是原生类型)创建目标对象, 其次检测是否有构造器,有的话,使用指定的构造器创建目标对象(构造器里面如果嵌套了查询或者ResultMap,则进行处理), 接着判断是否结果类型是接口或者具有默认构造器,则使用ObjectFactory创建默认目标对象, 最后判断是否可以应用自动映射,经过上面四层判断如果还不能创建目标对象则抛出异常。
如果在结果集中配置了constructor节点,将使用指定的构造器创建目标对象
// org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java
Object createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType, List<ResultMapping> constructorMappings,
List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) {
boolean foundValues = false;
// 遍历构造函数中的子节点
for (ResultMapping constructorMapping : constructorMappings) {
final Class<?> parameterType = constructorMapping.getJavaType();
final String column = constructorMapping.getColumn();
final Object value;
try {
// 如果是一个嵌套查询,则去查询结果
if (constructorMapping.getNestedQueryId() != null) {
value = getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix);
} else if (constructorMapping.getNestedResultMapId() != null) { // 存在嵌套结果
final ResultMap resultMap = configuration.getResultMap(constructorMapping.getNestedResultMapId());
value = getRowValue(rsw, resultMap);
} else {
final TypeHandler<?> typeHandler = constructorMapping.getTypeHandler();
value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(column, columnPrefix));
}
} catch (ResultMapException e) {
throw new ExecutorException("Could not process result for mapping: " + constructorMapping, e);
} catch (SQLException e) {
throw new ExecutorException("Could not process result for mapping: " + constructorMapping, e);
}
constructorArgTypes.add(parameterType);
constructorArgs.add(value);
foundValues = value != null || foundValues;
}
// 最终调用objectFactory的create方法创建实例
return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
}
通过 ObjectFactory 调用目标类的默认构造方法创建实例
// org/apache/ibatis/reflection/factory/DefaultObjectFactory.java
public <T> T create(Class<T> type) {
return create(type, null, null);
}
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
// 转换类型
Class<?> classToCreate = resolveInterface(type);
// 根据情况实例化对象
return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
}
// 根据是否传入了构造函数属性判断是否使用默认的构造函数进行实例化
private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
try {
Constructor<T> constructor;
if (constructorArgTypes == null || constructorArgs == null) {
constructor = type.getDeclaredConstructor();
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return constructor.newInstance();
}
constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
} catch (Exception e) {
// ......
}
}
判断是否可以应用自动映射
// org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java
private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) {
// 检测 <resultMap> 是否配置了 autoMapping 属性
if (resultMap.getAutoMapping() != null) {
// 返回 autoMapping 属性
return resultMap.getAutoMapping();
} else {
if (isNested) {
// 对于嵌套 resultMap,仅当全局的映射行为为 FULL 时,才进行自动映射
return AutoMappingBehavior.FULL == configuration.getAutoMappingBehavior();
} else {
// 对于普通的 resultMap,只要全局的映射行为不为 NONE,即可进行自动映射
return AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior();
}
}
}
首先检测resultMap 节点中是否存在了 autoMapping 属性,存在则直接取其值即可, 否则,根据结果集是否存在嵌套,是则只有当当全局的映射行为为 FULL 时,才进行自动映射, 否则,只要全局的映射行为不为 NONE,即可进行自动映射
如果可以应用自动映射,则通过返回结果集查找合适的构造方法创建实例
// org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java
private Object createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes,
List<Object> constructorArgs, tring columnPrefix) throws SQLException {
final Constructor<?>[] constructors = resultType.getDeclaredConstructors();
// 返回存在 AutomapConstructor 注解的构造函数
final Constructor<?> annotatedConstructor = findAnnotatedConstructor(constructors);
// 存在注解 AutomapConstructor 则使用该构造函数创建对象
if (annotatedConstructor != null) {
return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix, annotatedConstructor);
} else {
// 否则根据ResultSet返回的字段清单,遍历所有构造函数,找到匹配的。
for (Constructor<?> constructor : constructors) {
if (allowedConstructor(constructor, rsw.getClassNames())) {
return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix, constructor);
}
}
}
throw new ExecutorException("No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames());
}
首先判断是否存在被 AutomapConstructor 注解的构造函数,存在则使用其创建实例, 否则,遍历所有构造函数,根据ResultSet返回的字段清单,进行匹配,如果有则,使用这个构造函数实例化, 否则,抛出异常。
延迟加载时序图:
如果存在嵌套查询且开启了延迟加载,则为 resultObject 生成代理类
// org/apache/ibatis/executor/loader/javassist/JavassistProxyFactory.java
public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration,
ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
return EnhancedResultObjectProxyImpl.createProxy(
target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
}
委托EnhancedResultObjectProxyImpl创建代理
public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration,
ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
final Class<?> type = target.getClass();
// 获取 Javassist 回调方法拦截
EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(type, lazyLoader, configuration,
objectFactory, constructorArgTypes, constructorArgs);
// 创建代理
Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
PropertyCopier.copyBeanProperties(type, target, enhanced);
return enhanced;
}
首先创建 Javassist 回调类,该类实现 MethodHandler,类似JDK动态代理的InvocationHandler, 接着创建代理。
使用javassist生成代理
// org/apache/ibatis/executor/loader/javassist/JavassistProxyFactory.java
static Object crateProxy(Class<?> type, MethodHandler callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
// 创建代理工厂
ProxyFactory enhancer = new ProxyFactory();
// 设置父类,ProxyFactory将会动态生成一个类,继承该父类
enhancer.setSuperclass(type);
try {
type.getDeclaredMethod(WRITE_REPLACE_METHOD);
// ObjectOutputStream will call writeReplace of objects returned by writeReplace
if (log.isDebugEnabled()) {
log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
}
} catch (NoSuchMethodException e) {
// 实现 WriteReplaceInterface 接口,确保一定存在 writeReplace 方法
enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class});
} catch (SecurityException e) {
// nothing to do here
}
Object enhanced;
Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
try {
// 创建代理类并返回该类的实例。
enhanced = enhancer.create(typesArray, valuesArray);
} catch (Exception e) {
throw new ExecutorException("Error creating lazy proxy. Cause: " + e, e);
}
// 设置拦截处理
((Proxy) enhanced).setHandler(callback);
return enhanced;
}
访问延迟加载的属性时将会调用 MethodHandler 接口的 invoke方法
public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
final String methodName = method.getName();
try {
synchronized (lazyLoader) {
// 如果执行的是 writeReplace 方法
if (WRITE_REPLACE_METHOD.equals(methodName)) {
Object original;
if (constructorArgTypes.isEmpty()) {
original = objectFactory.create(type);
} else {
original = objectFactory.create(type, constructorArgTypes, constructorArgs);
}
PropertyCopier.copyBeanProperties(type, enhanced, original);
if (lazyLoader.size() > 0) {
return new JavassistSerialStateHolder(original, lazyLoader.getProperties(), objectFactory,
constructorArgTypes, constructorArgs);
} else {
return original;
}
} else {
if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
/*
* 如果 aggressive 为 true,或触发方法(比如 equals,hashCode 等)被调用,
* 则加载所有的所有延迟加载的数据
*
* <setting name="aggressiveLazyLoading" value="false"/>
* <setting name="lazyLoadTriggerMethods" value="equals,hashCode"/>
*/
if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
lazyLoader.loadAll();
} else if (PropertyNamer.isSetter(methodName)) {
final String property = PropertyNamer.methodToProperty(methodName);
// 如果使用者显示调用了 setter 方法,则将相应的延迟加载类从 loaderMap 中移除
lazyLoader.remove(property);
// 检测使用者是否调用 getter 方法
} else if (PropertyNamer.isGetter(methodName)) {
final String property = PropertyNamer.methodToProperty(methodName);
// 检测该属性是否有相应的 LoadPair 对象
if (lazyLoader.hasLoader(property)) {
// 执行延迟加载逻辑
lazyLoader.load(property);
}
}
}
}
}
// 调用被代理类的方法
return methodProxy.invoke(enhanced, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
执行延迟加载逻辑
// org/apache/ibatis/executor/loader/ResultLoaderMap.java
public boolean load(String property) throws SQLException {
// 从 loaderMap 中移除 property 所对应的 LoadPair
LoadPair pair = loaderMap.remove(property.toUpperCase(Locale.ENGLISH));
if (pair != null) {
// 加载结果
pair.load();
return true;
}
return false;
}
先从 loaderMap 中移除 property 所对应的 LoadPair, 接着加载结果。
加载结果
public void load() throws SQLException {
/* These field should not be null unless the loadpair was serialized.
* Yet in that case this method should not be called. */
if (this.metaResultObject == null) {
throw new IllegalArgumentException("metaResultObject is null");
}
if (this.resultLoader == null) {
throw new IllegalArgumentException("resultLoader is null");
}
// 调用重载方法
this.load(null);
}
public void load(final Object userObject) throws SQLException {
if (this.metaResultObject == null || this.resultLoader == null) {
if (this.mappedParameter == null) {
throw new ExecutorException("Property [" + this.property + "] cannot be loaded because "
+ "required parameter of mapped statement ["
+ this.mappedStatement + "] is not serializable.");
}
final Configuration config = this.getConfiguration();
final MappedStatement ms = config.getMappedStatement(this.mappedStatement);
if (ms == null) {
throw new ExecutorException("Cannot lazy load property [" + this.property
+ "] of deserialized object [" + userObject.getClass()
+ "] because configuration does not contain statement ["
+ this.mappedStatement + "]");
}
this.metaResultObject = config.newMetaObject(userObject);
this.resultLoader = new ResultLoader(config, new ClosedExecutor(), ms, this.mappedParameter,
metaResultObject.getSetterType(this.property), null, null);
}
/* We are using a new executor because we may be (and likely are) on a new thread
* and executors aren't thread safe. (Is this sufficient?)
*
* A better approach would be making executors thread safe. */
// 线程安全检测
if (this.serializationCheck == null) {
final ResultLoader old = this.resultLoader;
// 重新创建新的 ResultLoader 和 ClosedExecutor,ClosedExecutor 是非线程安全的
this.resultLoader = new ResultLoader(old.configuration, new ClosedExecutor(), old.mappedStatement,
old.parameterObject, old.targetType, old.cacheKey, old.boundSql);
}
/*
* 调用 ResultLoader 的 loadResult 方法加载结果,
* 并通过 metaResultObject 设置结果到实体类对象中
*/
this.metaResultObject.setValue(property, this.resultLoader.loadResult());
}
执行查询操作
// org/apache/ibatis/executor/loader/ResultLoader.java
public Object loadResult() throws SQLException {
// 执行关联查询
List<Object> list = selectList();
// 抽取结果
resultObject = resultExtractor.extractObjectFromList(list, targetType);
return resultObject;
}
先执行关联查询,接着抽取结果
执行关联查询
// org/apache/ibatis/executor/loader/ResultLoader.java
private <E> List<E> selectList() throws SQLException {
Executor localExecutor = executor;
if (Thread.currentThread().getId() != this.creatorThreadId || localExecutor.isClosed()) {
localExecutor = newExecutor();
}
try {
// 通过 Executor 就行查询
return localExecutor.<E> query(mappedStatement, parameterObject, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, cacheKey, boundSql);
} finally {
if (localExecutor != executor) {
localExecutor.close(false);
}
}
}
抽取结果
// org/apache/ibatis/executor/ResultExtractor.java
public Object extractObjectFromList(List<Object> list, Class<?> targetType) {
Object value = null;
// 目标类如果是 List 类型
if (targetType != null && targetType.isAssignableFrom(list.getClass())) {
value = list;
// 目标的对象工厂是Collection类型
} else if (targetType != null && objectFactory.isCollection(targetType)) {
value = objectFactory.create(targetType);
MetaObject metaObject = configuration.newMetaObject(value);
metaObject.addAll(list);
// 目标类如果是数组
} else if (targetType != null && targetType.isArray()) {
Class<?> arrayComponentType = targetType.getComponentType();
Object array = Array.newInstance(arrayComponentType, list.size());
if (arrayComponentType.isPrimitive()) {
for (int i = 0; i < list.size(); i++) {
Array.set(array, i, list.get(i));
}
value = array;
} else {
value = list.toArray((Object[])array);
}
} else {
// 返回数据超过一个,抛出异常
if (list != null && list.size() > 1) {
throw new ExecutorException("Statement returned more than one row, where no more than one was expected.");
} else if (list != null && list.size() == 1) {
value = list.get(0);
}
}
return value;
}
检测是否应该自动映射结果集
// org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java
private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) {
// 检测 <resultMap> 是否配置了 autoMapping 属性
if (resultMap.getAutoMapping() != null) {
// 返回 autoMapping 属性
return resultMap.getAutoMapping();
} else {
if (isNested) {
// 对于嵌套 resultMap,仅当全局的映射行为为 FULL 时,才进行自动映射
return AutoMappingBehavior.FULL == configuration.getAutoMappingBehavior();
} else {
// 对于普通的 resultMap,只要全局的映射行为不为 NONE,即可进行自动映射
return AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior();
}
}
}
首先检测 是否配置了 autoMapping 属性,配置了则获取 autoMapping 属性配置 否则,如果是一个嵌套 resultMap,仅当全局的映射行为为 FULL 时,才进行自动映射, 对于普通的 resultMap,只要全局的映射行为不为 NONE,即可进行自动映射
通过自动映射给属性赋值
// org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject,
String columnPrefix) throws SQLException {
// 获取 UnMappedColumnAutoMapping 列表
// UnMappedColumnAutoMapping 用于记录未配置在 <resultMap> 节点中的映射关系。
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (!autoMapping.isEmpty()) {
for (UnMappedColumnAutoMapping mapping : autoMapping) {
// 通过 TypeHandler 从结果集中获取指定列的数据
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')
// 通过元信息对象设置 value 到实体类对象的指定字段上
metaObject.setValue(mapping.property, value);
}
}
}
return foundValues;
}
首先获取 UnMappedColumnAutoMapping 列表,UnMappedColumnAutoMapping 用于记录未配置在 节点中的映射关系。 接着遍历列表,通过 TypeHandler 从结果集中获取指定列的数据, 最后通过元信息对象设置 value 到实体类对象的指定字段上。
获取 UnMappedColumnAutoMapping 列表
private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap,
MetaObject metaObject, String columnPrefix) throws SQLException {
final String mapKey = resultMap.getId() + ":" + columnPrefix;
// 从缓存中获取 UnMappedColumnAutoMapping 列表
List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
// 缓存未命中
if (autoMapping == null) {
autoMapping = new ArrayList<UnMappedColumnAutoMapping>();
// 从 ResultSetWrapper 中获取未配置在 <resultMap> 中的列名
final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
for (String columnName : unmappedColumnNames) {
String propertyName = columnName;
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;
}
}
// 将下划线形式的列名转成驼峰式,比如 AUTHOR_NAME -> authorName
final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
if (property != null && metaObject.hasSetter(property)) {
// 检测当前属性是否存在于 resultMap 中
if (resultMap.getMappedProperties().contains(property)) {
continue;
}
// 获取属性对于的类型
final Class<?> propertyType = metaObject.getSetterType(property);
if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
// 获取类型处理器
final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
// 封装上面获取到的信息到 UnMappedColumnAutoMapping 对象中
autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
} else {
configuration.getAutoMappingUnknownColumnBehavior()
.doAction(mappedStatement, columnName, property, propertyType);
}
} else {
/*
* 若 property 为空,或实体类中无 property 属性,此时无法完成
* 列名与实体类属性建立映射关系。针对这种情况,有三种处理方式,
* 1. 什么都不做
* 2. 仅打印日志
* 3. 抛出异常
* 默认情况下,是什么都不做
*/
configuration.getAutoMappingUnknownColumnBehavior()
.doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
}
}
// 写入缓存
autoMappingsCache.put(mapKey, autoMapping);
}
return autoMapping;
}
首先从缓存中获取 UnMappedColumnAutoMapping 列表,如果缓存未命中, 接着从 ResultSetWrapper 中获取未配置在 中的列名, 然后将下划线形式的列名转成驼峰式,比如 AUTHOR_NAME -> authorName,然后检查是否存在setter方法, 如果存在则接着获取属性对应的类型,然后获取类型处理器并封装上面获取到的信息到 UnMappedColumnAutoMapping 对象中。 最后写入缓存。
对于上面中如果属性不存在活不存在属性的setter方法或者不存在属性对应的类型,则会根据配置的“发现自动映射目标未知列(或者未知属性类型)的行为”进行相应处理。
ResultSetWrapper 中获取未配置在 中的列名
// org/apache/ibatis/executor/resultset/ResultSetWrapper.java
public List<String> getUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
List<String> unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
if (unMappedColumnNames == null) {
// 加载已映射与未映射列名
loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
// 获取未映射列名
unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
}
return unMappedColumnNames;
}
首先进行加载已映射与未映射列名,并将结果存入mappedColumnNamesMap和unMappedColumnNamesMap两个Map集合中, 接着通过指定key获取未映射列名列表集合。
加载已映射与未映射列名
// org/apache/ibatis/executor/resultset/ResultSetWrapper.java
private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
List<String> mappedColumnNames = new ArrayList<String>();
List<String> unmappedColumnNames = new ArrayList<String>();
final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);
// 为 <resultMap> 中的列名拼接前缀
final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);
/*
* 遍历 columnNames,columnNames 是 ResultSetWrapper 的成员变量,
* 保存了当前结果集中的所有列名
*/
for (String columnName : columnNames) {
final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);
// 检测已映射列名集合中是否包含当前列名
if (mappedColumns.contains(upperColumnName)) {
mappedColumnNames.add(upperColumnName);
} else {
// 将列名存入 unmappedColumnNames 中
unmappedColumnNames.add(columnName);
}
}
// 缓存列名集合
mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);
unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);
}
处理配置在 节点中的映射关系的属性的赋值
// org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject,
ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
// 获取已映射的列名
final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
// 获取 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;
}
/*
* 下面的 if 分支由三个或条件组合而成,三个条件的含义如下:
* 条件一:检测 column 是否为 {prop1=col1, prop2=col2} 形式,该
* 种形式的 column 一般用于关联查询
* 条件二:检测当前列名是否被包含在已映射的列名集合中,若包含则可进行数据集映射操作
* 条件三:多结果集相关
*/
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;
// 若获取到的值为 DEFERED,则延迟加载该值
} else if (value == DEFERED) {
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;
}
首先获取已映射的列名的集合,
接着从ResultMapping获取 ResultMapping集合,
然后遍历集合,处理每一个元素,接着从结果集中获取指定列的数据, 最后将获取到的值设置到实体类对象中。
从结果集中获取指定列的数据
private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping,
ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
if (propertyMapping.getNestedQueryId() != null) {
// 处理嵌套query类型的属性
return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
} else if (propertyMapping.getResultSet() != null) {
// 处理resultMap类型的属性,主要是:
// 1、维护记录cacheKey和父属性/记录对Map的关联关系,便于在处理嵌套ResultMap时很快可以找到所有需要处理嵌套结果集的父属性;
// 2、维护父属性和对应resultSet的关联关系。这两者都是为了在处理嵌套结果集是方便
addPendingChildRelation(rs, metaResultObject, propertyMapping); // TODO is that OK?
return DEFERED;
} else {
final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
// 拼接前缀
final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
// 从 ResultSet 中获取指定列的值
return typeHandler.getResult(rs, column);
}
}
首先判断是否存在集合的嵌套 Select 查询,有则处理嵌套query类型的属性, 如果不存在select属性,则查看是否存在 ResultSet 属性, 如果不存在select和ResultSet属性,则通过类型处理器,处理 ResultSet 中指定列的值。
处理嵌套query类型的属性
<resultMap id="blogResult" type="Blog">
<association property="author" column="author_id" javaType="Author" select="selectAuthor"/>
</resultMap>
即填充association属性,先看下解析结果
源码:
private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping,
ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
// 获取关联查询 id,id = 命名空间 + <association> 的 select 属性值
final String nestedQueryId = propertyMapping.getNestedQueryId();
final String property = propertyMapping.getProperty();
// 根据 nestedQueryId 获取 MappedStatement
final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
/*
* 生成关联查询语句参数对象,参数类型可能是一些包装类,Map 或是自定义的实体类,
* 具体类型取决于配置信息。以上面的例子为基础,下面分析不同配置对参数类型的影响:
* 1. <association column="author_id">
* column 属性值仅包含列信息,参数类型为 author_id 列对应的类型,这里为 Integer
*
* 2. <association column="{id=author_id, name=title}">
* column 属性值包含了属性名与列名的复合信息,MyBatis 会根据列名从 ResultSet 中
* 获取列数据,并将列数据设置到实体类对象的指定属性中,比如:
* Author{id=1, name="MyBatis 源码分析系列文章导读", age=null, ....}
* 或是以键值对 <属性, 列数据> 的形式,将两者存入 Map 中。比如:
* {"id": 1, "name": "MyBatis 源码分析系列文章导读"}
*
* 至于参数类型到底为实体类还是 Map,取决于关联查询语句的配置信息。比如:
* <select id="findAuthor"> -> 参数类型为 Map
* <select id="findAuthor" parameterType="Author"> -> 参数类型为实体类
*/
final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, propertyMapping, nestedQueryParameterType, columnPrefix);
Object value = null;
if (nestedQueryParameterObject != null) {
// 获取 BoundSql
final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
final Class<?> targetType = propertyMapping.getJavaType();
// 检查一级缓存是否保存了关联查询结果
if (executor.isCached(nestedQuery, key)) {
/*
* 从一级缓存中获取关联查询的结果,并通过 metaResultObject
* 将结果设置到相应的实体类对象中
*/
executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
value = DEFERED;
} else {
// 创建结果加载器
final ResultLoader resultLoader = new ResultLoader(
configuration, executor, nestedQuery, nestedQueryParameterObject,targetType, key, nestedBoundSql);
// 检测当前属性是否需要延迟加载
if (propertyMapping.isLazy()) {
// 添加延迟加载相关的对象到 loaderMap 集合中
lazyLoader.addLoader(property, metaResultObject, resultLoader);
value = DEFERED;
} else {
// 直接执行关联查询
value = resultLoader.loadResult();
}
}
}
return value;
}
首先提取关联查询中的一些属性, 然后根据 nestedQueryId 获取 MappedStatement, 接着从结果集中获取参数值,并绑定到BoundSql中, 对于第一次加载,会先创建结果加载器,然后检查当前属性是否需要延迟加载,如果需要,则将结果加载器添加延迟加载相关的对象到 loaderMap 集合中,否则直接执行查询
将结果加载器添加延迟加载相关的对象到 loaderMap 集合中
// org/apache/ibatis/executor/loader/ResultLoaderMap.java
private final Map<String, LoadPair> loaderMap = new HashMap<String, LoadPair>();
public void addLoader(String property, MetaObject metaResultObject, ResultLoader resultLoader) {
// 将属性名转为大写
String upperFirst = getUppercaseFirstProperty(property);
if (!upperFirst.equalsIgnoreCase(property) && loaderMap.containsKey(upperFirst)) {
throw new ExecutorException("Nested lazy loaded result property '" + property +
"' for query id '" + resultLoader.mappedStatement.getId() +
" already exists in the result map. The leftmost property of all lazy loaded properties must be unique within a result map.");
}
// 创建 LoadPair,并将 <大写属性名,LoadPair对象> 键值对添加到 loaderMap 中
loaderMap.put(upperFirst, new LoadPair(property, metaResultObject, resultLoader));
}
存储结果
// org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java
private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext,
Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
if (parentMapping != null) {
// 如果不是主记录,则链接到主记录
linkToParents(rs, parentMapping, rowValue);
} else {
// 存储结果
callResultHandler(resultHandler, resultContext, rowValue);
}
}
存储主记录
private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
// 设置结果到 resultContext 中
resultContext.nextResultObject(rowValue);
// 从 resultContext 获取结果,并存储到 resultHandler 中
((ResultHandler<Object>) resultHandler).handleResult(resultContext);
}
处理嵌套映射时序图:
处理嵌套映射 含有嵌套 resultMap 的列处理,通常是collection或者association
// org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java
private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler,
RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
final DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
// mybatis 分页处理
skipRows(rsw.getResultSet(), rowBounds);
// 前一次处理的记录,只有在映射语句的结果集无序的情况下有意义
Object rowValue = previousRowValue;
// 一直处理直到超出分页边界或者结果集处理完
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
//同主记录,先解析到鉴别器的最底层的ResultMap
final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
// 创建当前处理记录的rowKey,规则见下文
final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);
// 根据rowKey获取嵌套结果对象map中对应的值,因为在处理主记录时存储进去了,具体见上面addPending的流程图,所以partialObject一般不会为空
Object partialObject = nestedResultObjects.get(rowKey);
// issue #577 && #542
// 根据映射语句的结果集是否有序走不同的逻辑
if (mappedStatement.isResultOrdered()) {
if (partialObject == null && rowValue != null) {
// 对于有序结果集的映射语句,如果嵌套结果对象map中不包含本记录,则清空嵌套结果对象,因为此时嵌套结果对象之前的记录已经没有意义了
nestedResultObjects.clear();
// 添加记录到resultHandler的list属性中 或 如果是非主记录,添加到主记录对应属性的list或者object中
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
} else {
// 获取行数据
rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
if (partialObject == null) {
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
}
}
if (rowValue != null && mappedStatement.isResultOrdered() && shouldProcessMoreRows(resultContext, rowBounds)) {
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
previousRowValue = null;
} else if (rowValue != null) {
previousRowValue = rowValue;
}
}
首先处理主记录, 接着填充主记录的数据。
填充节点记录
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, String columnPrefix,
Object partialObject) throws SQLException {
final String resultMapId = resultMap.getId();
Object rowValue = partialObject;
if (rowValue != null) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
putAncestor(rowValue, resultMapId);
applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
ancestorObjects.remove(resultMapId);
} else {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
// 创建实体类对象,比如 Article 对象
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, true)) {
// 进行自动映射
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
// 处理属性映射,这里会识别出哪些属性需要nestQuery,哪些是nest ResultMap
// 根据 <resultMap> 节点中配置的映射关系进行映射
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
putAncestor(rowValue, resultMapId);
// 处理嵌套映射 含有嵌套resultMap的列处理,通常是collection或者association
foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
ancestorObjects.remove(resultMapId);
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
if (combinedKey != CacheKey.NULL_CACHE_KEY) {
nestedResultObjects.put(combinedKey, rowValue);
}
}
return rowValue;
}
首先创建实体类对象,接着属性进行自动映射及根据 节点中配置的映射关系进行映射,最后处理嵌套映射。
处理嵌套映射
private boolean applyNestedResultMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject,
String parentPrefix, CacheKey parentRowKey, boolean newObject) {
boolean foundValues = false;
// 遍历resultMap的子节点
for (ResultMapping resultMapping : resultMap.getPropertyResultMappings()) {
final String nestedResultMapId = resultMapping.getNestedResultMapId();
// 如果存在 ResultMap 属性 且不存在 ResultSet 则进行嵌套处理
if (nestedResultMapId != null && resultMapping.getResultSet() == null) {
try {
final String columnPrefix = getColumnPrefix(parentPrefix, resultMapping);
// 获取嵌套 ResultMap 对象。
final ResultMap nestedResultMap = getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix);
if (resultMapping.getColumnPrefix() == null) {
// try to fill circular reference only when columnPrefix
// is not specified for the nested result map (issue #215)
Object ancestorObject = ancestorObjects.get(nestedResultMapId);
if (ancestorObject != null) {
if (newObject) {
linkObjects(metaObject, resultMapping, ancestorObject); // issue #385
}
continue;
}
}
final CacheKey rowKey = createRowKey(nestedResultMap, rsw, columnPrefix);
final CacheKey combinedKey = combineKeys(rowKey, parentRowKey);
Object rowValue = nestedResultObjects.get(combinedKey);
boolean knownValue = rowValue != null;
instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject); // mandatory
if (anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw)) {
// 填充子节点行数据, nestedResultMap为嵌套 ResultMap
rowValue = getRowValue(rsw, nestedResultMap, combinedKey, columnPrefix, rowValue);
if (rowValue != null && !knownValue) {
// 赋值操作
linkObjects(metaObject, resultMapping, rowValue);
foundValues = true;
}
}
} catch (SQLException e) {
throw new ExecutorException("Error getting nested result map values for '" + resultMapping.getProperty() + "'. Cause: " + e, e);
}
}
}
return foundValues;
}
遍历节点的子节点,如果存在嵌套节点,则获取嵌套 ResultMap 对象并填充嵌套节点的行数据。
嵌套resultMap结构
通过 nestedResultMapId 属性判断是否寻找嵌套,存在则根据 nestedResultMapId 查询相应的 ResultMap 结构, 然后递归getRowValue。