该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对各位读者会不太友好,阅读前需要对 MyBatis 和 Spring 有一定的了解。比较适合刚接触,会使用但是一直没去探究底层的同学。
MyBatis 版本:3.5.6
MyBatis-Spring 版本:2.0.3
MyBatis-Spring-Boot-Starter 版本:2.1.4
该系列其他文档请查看:《 MyBatis 系列 - 导读》
在前面一系列的文档中,我已经分析了 MyBatis 的基础支持层以及整个的初始化过程,此时 MyBatis 已经处于就绪状态了,等待使用者发号施令了
那么接下来我们来看看它执行SQL的整个过程,该过程比较复杂,涉及到二级缓存,将返回结果转换成 Java 对象以及延迟加载等等处理过程,这里将一步一步地进行分析:
MyBatis中SQL执行的整体过程如下图所示:
在 SqlSession 中,会将执行 SQL 的过程交由Executor
执行器去执行,过程大致如下:
DefaultSqlSessionFactory
创建与数据库交互的 SqlSession
“会话”,其内部会创建一个Executor
执行器对象Executor
执行器通过StatementHandler
创建对应的java.sql.Statement
对象,并通过ParameterHandler
设置参数,然后执行数据库相关操作KeyGenerator
先设置自增键,然后返回受影响的行数ResultSet
结果集对象包装成ResultSetWrapper
,然后通过DefaultResultSetHandler
对结果集进行映射,最后返回 Java 对象上面还涉及到一级缓存、二级缓存和延迟加载等其他处理过程
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
对象,可以回到《MyBatis - SQL执行过程(十三)StatementHandler》的BaseStatementHandler小节中的构造方法
的第3
步可以看到resultSetHandler
的handleResultSets(Statement stmt)
方法,对结果集进行映射,转换成Java对象并返回因为在
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对象中映射的列名集合,也就是我们在
标签下的子标签配置的column
属性unMappedColumnNamesMap
:保存每个ResultMap对象中未映射的列名集合,也就是没有在
标签下配置过,但是查询结果返回了在构造方法中,会初始化上面的columnNames
、classNames
和jdbcTypes
属性
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, TypeHandler>> typeHandlerMap
属性中获取类型处理器typeHandlerRegistry
注册表获取classNames
中拿到结果集中该列的Java Type,然后在从typeHandlerRegistry
注册表获取ObjectTypeHandler
typeHandlerMap
缓存中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 中配置的所有列名,并添加前缀
* 如果在 上面配置的是 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
中配置的所有列名,并添加前缀
如果在上面配置的是
resultType
属性,则返回的是空集合,因为它创建的ResultMap
对象中只有Java Type属性
遍历结果集中所有的列名,如果在
标签中的子标签配置的column
属性有包含这个列名,则属于映射的列名
否则就属于未被映射的列名
org.apache.ibatis.executor.resultset.ResultSetHandler
:结果集映射接口,代码如下:
public interface ResultSetHandler {
/**
* 处理 {@link java.sql.ResultSet} 成映射的对应的结果
*
* @param stmt Statement 对象
* @param 泛型
* @return 结果数组
* @throws SQLException SQL异常
*/
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
/**
* 处理 {@link java.sql.ResultSet} 成 Cursor 对象
*
* @param stmt Statement 对象
* @param 泛型
* @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;
}
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;
}
}
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
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
// <2> 获取 ResultSet 对象,并封装成 ResultSetWrapper
ResultSetWrapper rsw = getFirstResultSet(stmt);
/*
* <3> 获得当前 MappedStatement 对象中的 ResultMap 集合,XML 映射文件中 标签生成的
* 或者 配置 "resultType" 属性也会生成对应的 ResultMap 对象
* 在 标签配置 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
对象
获取 ResultSet
对象,并封装成 ResultSetWrapper
获得当前 MappedStatement 对象中的 ResultMap
集合,XML 映射文件中
标签生成的,或者 配置 “resultType” 属性也会生成对应的 ResultMap
对象
在 标签配置
ResultMap
属性时,可以以逗号分隔配置多个,如果返回多个 ResultSet 则会一一映射,通常配置一个
如果有返回结果,但是没有 ResultMap
接收对象则抛出异常
调用handleResultSet
方法,完成结果集的映射,全部转换的 Java 对象,保存至 multipleResults
集合中,或者 this.resultHandler
中(用户自定的,通常不会)
获取 resultSets 多结果集属性的配置,存储过程中使用,暂时忽略,本文暂不分析
完成结果集映射的任务还是交给了2.handleResultSet方法
handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List
方法,处理结果集
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());
}
}
6
步ResultHandler
结果处理器
DefaultResultHandler
默认结果处理器,就是使用一个List集合保存转换后的Java对象handleRowValues
方法,处理结果集,进行一系列的处理,完成映射,将结果保存至 DefaultResultHandler 中multipleResults
中ResultHandler
结果处理器,和第2
步的区别在于,处理后的Java对象不会保存在multipleResults
中,仅保存在ResultHandler
中,用户可通过它获取通常我们不会自定义结果处理器的,所以第4
步本文暂不分析,我们来看到第2
步,最终还是交给了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 对象
* 该对象的 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 对象,该对象的 id 会设置到 ResultMapping
的 nestedResultMapId
属性中,这就属于内嵌的 ResultMap
resultHandler
或者设置到 parentMapping
(存储过程相关,本文暂不分析)的对应属性中,这里会对内嵌的 ResultMap 进行处理,调用handleRowValuesForNestedResultMap
方法处理结果集,进行映射,生成返回结果,保存至 resultHandler
或者设置到 parentMapping
(存储过程相关,本文暂不分析)的对应属性中,调用handleRowValuesForSimpleResultMap
方法
这里先来看到第2
步中的4.handleRowValuesForSimpleResultMap方法,因为这个处理的情况相比第1
步调用的方法简单些
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 可能使用到了 标签,需要根据不同的值映射不同的 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 可能使用到了
标签,需要根据不同的值映射不同的 ResultMap
如果存在 Discriminator
鉴别器,则根据当前记录选择对应的 ResultMap,会一直嵌套处理
调用getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix)
方法,从结果集中获取到返回结果对象,进行映射,比较复杂,关键方法!!!
调用storeObject
方法,将返回结果对象保存至 resultHandler
,或者设置到父对象 parentMapping
(存储过程相关,本文暂不分析)的对应属性中
对于第3
、4
、5
步的三个方法,我们一个一个来看
resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix)
方法,如果存在
鉴别器,则进行处理,选择对应的 ResultMap
,会一直嵌套处理
public ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix)
throws SQLException {
// 记录已经处理过的 ResultMap 的 id
Set<String> pastDiscriminators = new HashSet<>();
// <1> 获取 ResultMap 中的 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
鉴别器,
标签会被解析成该对象
调用getDiscriminatorValue
方法,获取当前记录中该列的值,通过类型处理器转换成了对应的类型,方法如下:
private Object getDiscriminatorValue(ResultSet rs, Discriminator discriminator, String columnPrefix) throws SQLException {
// 获取 标签对应的的 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
Discriminator
鉴别结果没有对应的 ResultMap,则直接跳过
返回最终使用的 ResultMap 对象
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
,便于操作useConstructorMappings
表示是否在构造方法中使用了参数映射shouldApplyAutomaticMappings
方法,检测是否需要自动映射,就是对未被映射的列进行处理applyAutomaticMappings
方法,从结果集中将未被映射的列值设置到返回结果 metaObject
中,返回是否映射成功(设置了1个或以上的属性值)applyPropertyMappings
方法,从结果集中将 ResultMap 中需要映射的列值设置到返回结果 metaObject
中,返回是否映射成功(设置了1个或以上的属性值)returnInstanceForEmptyRow
全局配置(默认为false)返回空对象还是 null返回该结果对象rowValue
我们逐步来看上面的第2
、3.3
、3.4
、3.5
所调用的方法
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
重载方法
createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List
方法,找到构造方法,创建一个实例对象
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);
// 获取 标签下构造函数的入参信息,可以通过这些入参确认一个构造函数
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
/*
* 创建结果对象,分为下面4种场景:
* 1. 结果集只有一列,且存在对应的 TypeHandler 类型处理器,例如返回 java.lang.String
* 2. 标签下配置的 标签下的构造函数参数信息不为空
* 3. 返回类型为接口,或者有默认的构造方法
* 4. 找到合适的构造方法
*/
if (hasTypeHandlerForResultObject(rsw, resultType)) { // 场景1
// 将该列转换成对应 Java Type 的值,然后返回
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
} else if (!constructorMappings.isEmpty()) { // 场景2
// 根据 标签下的构造方法入参配置,尝试从结果集中获取入参值,并创建返回结果的实例对象
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 的值,然后返回
标签下配置的
标签下的构造函数参数信息不为空
则调用createParameterizedResultObject
方法,根据
返回类型为接口,或者有默认的构造方法
则通过实例工厂objectFactory
,使用默认无参构造方法创建返回结果的实例对象
找到合适的构造方法
则调用createByConstructorSignature
方法,找到合适的构造方法并创建返回结果对象
好的,接下来我们又要看到第1
、2
、4
步调用的三个方法了
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()) { // 配置了
// 获取 标签下的配置信息
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 对象createParameterizedResultObject(ResultSetWrapper rsw, Class resultType, List
方法
根据
标签下的
标签配置的参数构建一个实例对象
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;
}
标签配置的参数对应的值,这里又可能存在以下三种情况:
getNestedQueryConstructorValue
方法,获取到该属性值getRowValue
方法,从该结果集中获取到嵌套 ResultMap 对应的值,回到了4.2getRowValue方法objectFactory
实例工厂,根据上面配置的入参信息构建一个实例对象这里我们又要进入第1.1
步的方法
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;
}
prepareParameterForNestedQuery
方法,进去后发现又得两层方法,这里就不再展开了,比较简单,可以先参考的我的注释查看,在后续还会调用该方法,再进行解析createByConstructorSignature(ResultSetWrapper rsw, Class resultType, List
方法,尝试找一个合适的构造方法构建一个实例对象
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
方法,创建一个实例对象createUsingConstructor
方法,创建一个实例对象上面需要调用的createUsingConstructor
方法比较简单,这里就不再展开了,大致逻辑就是从结果集中获取到该构造方法所有的入参,然后构建一个实例对象
shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested)
方法,检测是否需要自动映射(对未被映射的列进行处理)
private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) {
/*
* 获取 中的 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();
}
}
}
中的autoMapping
配置不为空,则返回该配置PARTIAL
,也就是不是嵌套映射则需要对未被映射的列进行处理,嵌套查询的话不会对未被映射的列进行处理(需要配置为FULL
)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
对象(包含列名、属性名、类型处理器、是否为原始类型)这里我们来看到createAutomaticMappings
方法
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;
}
autoMappingsCache
缓存中获取该 ResultMap 对应的 UnMappedColumnAutoMapping
集合 autoMapping
,没有的话才进行接下来的解析UnMappedColumnAutoMapping
对象,如果返回对象中有该属性的 setter 方法
UnMappedColumnAutoMapping
对象,设置列名、属性名、类型处理器、是否为原始类型,添加到autoMapping
集合中NONE
,不做任何行为NONE
,不做任何行为autoMapping
,并添加到autoMappingsCache
缓存中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;
}
mappedColumnNames
column
属性为{prop1:col1,prop2:col2}
这种形式,一般就是嵌套子查询,表示将col1和col2的列值设置到嵌套子查询的入参对象的prop1和prop2属性中getPropertyMappingValue
方法,从结果集中获取到对应的属性值value
DEFERRED
占位符(延迟加载),则跳过我们来看看getPropertyMappingValue
方法
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
方法, 多结果集处理,延迟加载,返回占位符,本文暂不分析我们这里继续看到第1
步中的调用的方法
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
步中调用的方法
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
步调用的方法
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
方法,很简单,点击去看一下就知道了List composites
组合字段column
列名从该结果集中获取值parameterObject
子查询入参对象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));
}
storeObject(ResultHandler resultHandler, DefaultResultContext
方法
将返回结果 对象保存至 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
步调用的方法
callResultHandler(ResultHandler resultHandler, DefaultResultContext
方法
普通映射,将结果对象保存到 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 源码分析》
--------------最后感谢大家的阅读,愿大家技术越来越流弊!--------------
--------------也希望大家给我点支持,谢谢各位大佬了!!!--------------