MyBatis 可以将查询结果,即结果集 ResultSet 自动映射成实体类对象。这样使用者就无
需再手动操作结果集,并将数据填充到实体类对象中。这可大大降低开发的工作量,提高工
作效率。

结果集的处理工作由结果集处理器ResultSetHandler执行。ResultSetHandler是一个接口,它只有一个实现类 DefaultResultSetHandler。结果集的处理入口方法是handleResultSets,下面来看一下该方法的实现。

handleResultSets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public List<Object> handleResultSets(Statement stmt) throws SQLException {
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
// 获取第一个结果集
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
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) {...}

return collapseSingleResultList(multipleResults);
}

private ResultSetWrapper getFirstResultSet(Statement stmt)
throws SQLException {
// 获取结果集
ResultSet rs = stmt.getResultSet();
while (rs == null) {
/*
* 移动 ResultSet 指针到下一个上,有些数据库驱动可能需要使用者
* 先调用 getMoreResults 方法,然后才能调用 getResultSet 方法
* 获取到第一个 ResultSet
*/
if (stmt.getMoreResults()) {
rs = stmt.getResultSet();
} else {
if (stmt.getUpdateCount() == -1) {
break;
}
}
}
/*
* 这里并不直接返回 ResultSet,而是将其封装到 ResultSetWrapper 中。
* ResultSetWrapper 中包含了 ResultSet 一些元信息,比如列名称、
* 每列对应的 JdbcType、以及每列对应的 Java 类名(class name,譬如
* java.lang.String)等。
*/
return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}

该方法首先从 Statement 中获取第一个结果集,然后调用 handleResultSet 方法对
该结果集进行处理。一般情况下,如果我们不调用存储过程,不会涉及到多结果集的问题。

单结果集的处理逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, 
List<Object> multipleResults, ResultMapping parentMapping)
throws SQLException {
try {
if (parentMapping != null) {
// 多结果集相关逻辑,不分析了
handleRowValues(rsw, resultMap,
null, RowBounds.DEFAULT, parentMapping);
} else {
/*
* 检测 resultHandler 是否为空。ResultHandler 是一个接口,使用者可
* 实现该接口,这样我们可以通过 ResultHandler 自定义接收查询结果的
* 动作。比如我们可将结果存储到 List、Map 亦或是 Set,甚至丢弃,
* 这完全取决于大家的实现逻辑。
*/
if (resultHandler == null) {
// 创建默认的结果处理器
DefaultResultHandler defaultResultHandler =
new DefaultResultHandler(objectFactory);
// 处理结果集的行数据
handleRowValues(rsw, resultMap,
defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
// 处理结果集的行数据
handleRowValues(rsw,resultMap,resultHandler,rowBounds,null);
}
}
} finally {
closeResultSet(rsw.getResultSet());
}
}

出镜率最高的 handleRowValues 方法,该方法用于处理结果集中的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, 
ResultHandler<?> resultHandler,RowBounds rowBounds,
ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
// 处理嵌套映射,关于嵌套映射本文就不分析了
handleRowValuesForNestedResultMap(rsw,
resultMap, resultHandler, rowBounds, parentMapping);
} else {
// 处理简单映射
handleRowValuesForSimpleResultMap(rsw,
resultMap, resultHandler, rowBounds, parentMapping);
}
}

handleRowValues 方法中针对两种映射方式进行了处理。一种是嵌套映射,另一种是简
单映射。本文所说的嵌套查询是指ResultMap中嵌套了一个ResultMap

handleRowValuesForSimpleResultMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, 
ResultMap resultMap, ResultHandler<?> resultHandler,RowBounds rowBounds,
ResultMapping parentMapping) throws SQLException {
DefaultResultContext<Object> resultContext =
new DefaultResultContext<Object>();
// 根据 RowBounds 定位到指定行记录
skipRows(rsw.getResultSet(), rowBounds);
// 检测是否还有更多行的数据需要处理
while (shouldProcessMoreRows(resultContext, rowBounds) &&
rsw.getResultSet().next()) {
// 获取经过鉴别器处理后的 ResultMap
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(
rsw.getResultSet(), resultMap, null);
// 从 resultSet 中获取结果
Object rowValue = getRowValue(rsw, discriminatedResultMap);
// 存储结果
storeObject(resultHandler, resultContext,
rowValue, parentMapping, rsw.getResultSet());
}
}


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();
}
}
}

上面方法的逻辑较多,这里简单总结一下。如下:

  1. 根据 RowBounds 定位到指定行记录
  2. 循环处理多行数据
  3. 使用鉴别器处理 ResultMap
  4. 映射 ResultSet,得到映射结果 rowValue
  5. 存储结果

MyBatis 默认提供了 RowBounds 用于分页,从上面的代码中可以看出,这并非是一个高
效的分页方式。除了使用 RowBounds,还可以使用一些第三方分页插件进行分页。

getRowValue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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;
}
// 根据 <resultMap> 节点中配置的映射关系进行映射
foundValues = applyPropertyMappings(
rsw, resultMap, metaObject, lazyLoader, null) || foundValues;

foundValues = lazyLoader.size() > 0 || foundValues;
rowValue=foundValues || configuration.isReturnInstanceForEmptyRow()
? rowValue : null;
}
return rowValue;
}

上面的方法中的重要逻辑已经注释出来了,这里再简单总结一下。如下:

  1. 创建实体类对象
  2. 检测结果集是否需要自动映射,若需要则进行自动映射
  3. 按中配置的映射关系进行映射

创建实体类对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// -☆- DefaultResultSetHandler
private Object createResultObject(ResultSetWrapper rsw,
ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
this.useConstructorMappings = false;
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 生成代理类
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();
return resultObject;
}

创建好实体类对后,还需要对中配置的映射信息进行检测。若发现
有关联查询,且关联查询结果的加载方式为延迟加载,此时需为实体类生成代理类。

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/** 作者类 */
public class Author {
private Integer id;
private String name;
private Integer age;
private Integer sex;
}

/** 文章类 */
public class Article {
private Integer id;
private String title;
// 一对一关系
private Author author;
private String content;
}

Article 对象中的数据由一条 SQL 从 article 表中查询。Article 类有一个 author 字
段,该字段的数据由另一条 SQL 从 author 表中查出。

我们在将 article 表的查询结果填充到Article类对象中时,并不希望MyBaits立即执行另一条 SQL 查询 author 字段对应的数据。而是期望在我们调用article.getAuthor()方法时,MyBaits 再执行另一条 SQL 从 author 表中查询出所需的数据��

若如此,我们需要改造 getAuthor 方法,以保证调用该方法时可让 MyBatis执行相关的 SQL。

createResultObject 重载方法的逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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);
} else if (!constructorMappings.isEmpty()) {
// 通过 <constructor> 节点配置的映射信息从 ResultSet 中ᨀ取数据,
// 然后将这些数据传给指定构造方法,即可创建实体类对象
return createParameterizedResultObject(rsw, resultType,
constructorMappings, constructorArgTypes,
constructorArgs, columnPrefix);
} else if(resultType.isInterface() || metaType.hasDefaultConstructor()){
// 通过 ObjectFactory 调用目标类的默认构造方法创建实例
return objectFactory.create(resultType);
} else if (shouldApplyAutomaticMappings(resultMap, false)) {
// 通过自动映射查找合适的构造方法创建实例
return createByConstructorSignature(rsw, resultType,
constructorArgTypes, constructorArgs, columnPrefix);
}
throw new ExecutorException("……");
}

createResultObject 方法中包含了 4 种创建实体类对象的方式。一般情况下,若无特殊要
求,MyBatis 会通过 ObjectFactory 调用默认构造方法创建实体类对象。ObjectFactory 是一个接口,大家可以实现这个接口,以按照自己的逻辑控制对象的创建过程。至此,实体类对象创建好了,接下里要做的事情是将结果集中的数据映射到实体类对象中。

结果集映射

MyBatis 中,结果集自动映射有三种等级。这三种等级官方文档上有所说明,这里直
接引用一下。如下:

  • NONE 禁用自动映射。仅设置手动映射属性
  • PARTIAL 将自动映射结果除了那些有内部定义内嵌结果映射的(joins)
  • FULL 自动映射所有

除了以上三种等级,我们还可以显示配置置resultMap节点的 autoMapping 属性,以启用
或者禁用指定 ResultMap 的自动映射设定。

自动映射相关的逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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();
}
}
}

shouldApplyAutomaticMappings 方法用于检测是否应为当前结果集应用自动映射。检测
结果取决于resultMap节点的 autoMapping 属性,以及全局自动映射行为。

MyBatis 是如何进行自动映射的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap
resultMap, MetaObject metaObject, String columnPrefix)
throws SQLException {
// 获取 UnMappedColumnAutoMapping 列表
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(
rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (!autoMapping.isEmpty()) {
for (UnMappedColumnAutoMapping mapping : autoMapping) {
// 通过 TypeHandler 从结果集中获取指定列的数据
final Object value = mapping.typeHandler
.getResult(rsw.getResultSet(), mapping.column);
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() &&
!mapping.primitive)) {
// 通过元信息对象设置 value 到实体类对象的指定字段上
metaObject.setValue(mapping.property, value);
}
}
}
return foundValues;
}

首先是获取UnMappedColumnAutoMapping 集合,然后遍历该集合,并通过 TypeHandler 从结果集中获取数据,最后再将获取到的数据设置到实体类对象中。

UnMappedColumnAutoMapping用于记录未配置在resultMap节点中的映射关系。该类定义在 DefaultResultSetHandler 内部,它的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static class UnMappedColumnAutoMapping {
private final String column;
private final String property;
private final TypeHandler<?> typeHandler;
private final boolean primitive;

public UnMappedColumnAutoMapping(String column, String property,
TypeHandler<?> typeHandler, boolean primitive) {
this.column = column;
this.property = property;
this.typeHandler = typeHandler;
this.primitive = primitive;
}
}

获取 UnMappedColumnAutoMapping 集合的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// -☆- DefaultResultSetHandler
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()) {
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;
}
  1. 从 ResultSetWrapper 中获取未配置在中的列名
  2. 遍历上一步获取到的列名列表
  3. 若列名包含列名前缀,则移除列名前缀,得到属性名
  4. 将下划线形式的列名转成驼峰式
  5. 获取属性类型
  6. 获取类型处理器
  7. 创建 UnMappedColumnAutoMapping 实例

第一个步骤的逻辑,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// -☆- ResultSetWrapper
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;
}

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);
}
  • 首先是从当前数据集中获取列名集合
  • 然后获取中配置的列名集合。
  • 之后遍历数据集中的列名集合,并判断列名是否被配置在了节点中。
    • 若配置了,则表明该列名已有映射关系,此时该列名存入 mappedColumnNames 中。
    • 若未配置,则表明列名未与实体类的某个字段形成映射关系,此时该列名存入 unmappedColumnNames 中。
  • 这样,列名的分拣工作就完成了。

如上图所示:

  1. 实体类 Author 的id和name字段与列名id和name被配置在了resultMap中,它们之间形成了映射关系。
  2. 列名age、sex和email未配置在中,因此未与Author中的字段形成映射,所以他们最终都被放入了unMappedColumnNames 集合中。

MyBatis 是如何将结果集中的数据填充到已映射的实体类字段中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// -☆- DefaultResultSetHandler
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) {
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);
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())) {
// 将获取到的值设置到实体类对象中
metaObject.setValue(property, value);
}
}
}
return foundValues;
}

private Object getPropertyMappingValue(ResultSet rs, MetaObject
metaResultObject, ResultMapping propertyMapping, ResultLoaderMap
lazyLoader, String columnPrefix) throws SQLException {
if (propertyMapping.getNestedQueryId() != null) {
// 获取关联查询结果,下一节分析
return getNestedQueryMappingValue(rs, metaResultObject,
propertyMapping, lazyLoader, columnPrefix);
} else if (propertyMapping.getResultSet() != null) {
addPendingChildRelation(rs, metaResultObject, propertyMapping);
return DEFERED;
} else {
final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
// 拼接前缀
final String column = prependPrefix(propertyMapping.getColumn(),
columnPrefix);
// 从 ResultSet 中获取指定列的值
return typeHandler.getResult(rs, column);
}
}