// 结果集处理器
public interface ResultSetHandler {
// 处理单个或多个多结果集
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
// 处理游标结果集
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
// 处理存储过程的输出参数
void handleOutputParameters(CallableStatement cs) throws SQLException;
// 默认的结果集处理器实现
public class DefaultResultSetHandler implements ResultSetHandler {
// 延迟加载的固定标识值,如果返回该值,就表示需要延迟加载
public static Object DEFERRED = new Object();
// 执行器
public Executor executor;
// 配置信息
public Configuration configuration;
// 映射语句信息
public MappedStatement mappedStatement;
// 分页条件
public RowBounds rowBounds;
// 参数处理器
public ParameterHandler parameterHandler;
// 结果处理器,可以处理每一行记录
public ResultHandler<?> resultHandler;
// SQL的绑定信息
public BoundSql boundSql;
// 类型转换器注册表
public TypeHandlerRegistry typeHandlerRegistry;
// 对象工厂
public ObjectFactory objectFactory;
// 反射工厂
public ReflectorFactory reflectorFactory;
// 嵌套的ResultMap的对象的缓存,所有嵌套属性的缓存对象
public Map<CacheKey, Object> nestedResultObjects = new HashMap<>();
/**
* 嵌套ResultMap的父对象缓存,主要用于复杂的结果集映射,缓存上一层的父对象
* 例如,正在映射allResultMap -> User , 由于现在是处理嵌套映射,那么当映射Role的时候使用的父对象必然是在User对象
* 缓存父对象主要是为了解决循环依赖,例如在User中包含Role,在Role中又包含User
* <pre>
* 案例
* <resultMap id="allResultMap" type="Users">
* <association property="roles" javaType="Roles" resultMap="roleResultMap">
* <id property="id" column="id"/>
* 此时,Roles对象中,又引用了allResultMap,也就是User对象这个ResultMap
* <association property="users" javaType="Users" resultMap="allResultMap"/>
* </association>
* </resultMap>
* </pre>
* 因此在执行User的嵌套属性映射Role时候,需要将User对象缓存,到时候Role映射完之后,设置到该User对象中,如果不缓存,每次创建对象
* <pre>
* 例如: class User{
* Role role;
* }
* class Role{
* User user;
* }
* </pre>
*/
public Map<String, Object> ancestorObjects = new HashMap<>();
// 前一行的的值,用于处理多结果集
public Object previousRowValue;
/**
* 多个结果集的情况下使用
* 正在映射的属性列指定了要从某个结果集中获取数据,但是该结果集暂时没有执行到,因此需要暂存这个属性的映射信息
* key: 指定的结果集名称,value: 该属性的映射信息
* <pre>
* <select id="selectBlog" resultSets="blogs,authors" resultMap="blogResult" statementType="CALLABLE">
* {call getBlogsAndAuthors(#{id,jdbcType=INTEGER,mode=IN})}
* </select>
*
* <resultMap id="blogResult" type="Blog">
* <id property="id" column="id" />
* <association property="author" javaType="Author" resultSet="authors" resultMap="authorMap" column="author_id" foreignColumn="id">
* <id property="id" column="id"/>
* </association>
* </resultMap>
* </pre>
*/
// key: 结果集名称,该结果集需要与那个字段进行映射
public Map<String, ResultMapping> nextResultMaps = new HashMap<>();
// 缓存指定了要使用某一个结果集进行结果映射但是该结果集还未被处理的那些字段信息
// 例如: 存在多个结果集A1,A2,处理A1结果集的时候,有resultMap中的字段指定了要使用A2的结果集中的字段进行映射
// 此时,会为A1结果集创建一个缓存Key,缓存该结果集挂起(也就是指定了A2结果的)的所有的字段映射信息
public Map<CacheKey, List<PendingRelation>> pendingRelations = new HashMap<>();
// 缓存指定的resultMap中,可以自动映射成功的列名信息
// key: resultMapId[前缀],所有映射成功的列名
public Map<String, List<UnMappedColumnAutoMapping>> autoMappingsCache = new HashMap<>();
// 保存所有通过构造函数名称自动映射的列名信息
// key: resultMap.getId() + ":" + columnPrefix
public Map<String, List<String>> constructorAutoMappingColumns = new HashMap<>();
// 临时标记标志,表示当前结果对象使用了构造函数映射
public boolean useConstructorMappings;
// 处理多结果集,多结果集需要驱动支持
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
// 兼容多结果集的处理方案,每一个元素都是一个结果集中的结果
final List<Object> multipleResults = new ArrayList<>();
// 结果集的数量
int resultSetCount = 0;
// 获取查询返回结果集中的第一个结果集对象,因为可能存在多个结果集的情况
ResultSetWrapper rsw = this.getFirstResultSet(stmt);
// 所有该映射语句使用的所有resultMap,可能有多个
List<ResultMap> resultMaps = mappedStatement.resultMaps;
// resultMap个数
int resultMapCount = resultMaps.size();
// 如果存在结果集的情况下,必须需要resultMap,否则抛出异常
// 正常情况下都会有
this.validateResultMapsCount(rsw, resultMapCount);
/**
* 将结果集与resultMap进行映射,如果只有一个结果集和一个resultMap,这也是最常见的映射,那么直接拿当前结果集使用resultMap进行映射就行
* 但是,结果集和resultMap都可以有多个,那么如何映射呢?
* 1. 如果结果集与resultMap的数量一致的话,那么,一个结果集使用一个resultMap进行映射,处理完之后,此时while循环终止,rsw结果集对象为空
* 2. 如果结果集与resultMap的数量不一致的话,又有两种情况
* 2.1 结果集多,resultMap少,一个结果集使用一个resultMap进行映射,当while循环终止的时候,此时多出来结果集对象还未处理,这种情况下面会有一个循环专门处理
* 2.2 resultMap多,结果集少,一个结果集使用一个resultMap进行映射,当while循环终止的时候,rsw结果集对象为空,多出来的resultMap会被忽略
*/
while (rsw != null && resultMapCount > resultSetCount) {
// 获取该映射语句的ResultMap结果集映射信息
ResultMap resultMap = resultMaps.get(resultSetCount);
// 处理结果集的映射关系
this.handleResultSet(rsw, resultMap, multipleResults, null);
// 获取下一个结果集进行处理
rsw = getNextResultSet(stmt);
// 处理结果集后的清理上一次的结果映射相关的缓存数据
cleanUpAfterHandlingResultSet();
// 处理的结果集数量+1
resultSetCount++;
}
/**
* 处理上面注释中2.1中剩下的结果集对象,由于上一个结果集进行字段映射的时候,某字段可能指定了要与当前暂未进行映射的结果集来映射
* 例如: 该sql会产生两个结果集,分别是blogs,authors,首先肯定是将结果集blogs与resultMap(blogResult)进行映射
* 在这个过程中,blogResult映射author字段的时候,该字段指定了要使用authors来映射,那他是如何完成的呢?
* 1. 首先在上面这个循环会处理blogs这个结果集与blogResult这个ResultMap进行映射,映射到author字段的时候,发现该字段要使用authors进行映射
* 但是此时当前结果集blogs还没有处理完,因此将author(字段映射)->authors(结果集)的映射关系进行了挂起
* 而挂起的字段映射到结果集的关系保存到{@link nextResultMaps}这个Map中,具体挂起的逻辑参考
* {@link ResultSetHandler.DefaultResultSetHandler#getPropertyMappingValue)
* {@link ResultSetHandler.DefaultResultSetHandler#addPendingChildRelation)
* <pre>
* <select id="selectBlog" resultSets="blogs,authors" resultMap="blogResult" statementType="CALLABLE">
* {call getBlogsAndAuthors(#{id,jdbcType=INTEGER,mode=IN})}
* </select>
*
* <resultMap id="blogResult" type="Blog">
* <association property="author" javaType="Author" resultSet="authors" resultMap="authorMap" column="author_id" foreignColumn="id"/>
* </resultMap>
* </pre>
*/
// 获取该映射语句设置的结果集对象
String[] resultSets = mappedStatement.resultSets;
// 如果设置了,就需要处理
if (resultSets != null) {
// 当结果集对象不为空,表示结果集个数多于resultMap数量,就要处理上一个结果集映射中,挂起的字段映射
while (rsw != null && resultSetCount < resultSets.length) {
// 根据当前结果集的名称,找到在上一个结果集映射的过程中,依赖该结果集而挂起的映射字段信息
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
// 如果存在引用当前结果集被挂起的字段映射信息
if (parentMapping != null) {
// 因为能被挂起的只有association或者colllection,这两个标签才能个指定从结果集中映射
// 而association使用的结果集是嵌套的resultMap,因此parentMapping.nestedResultMapId就是获取在association指定的resultMap
String nestedResultMapId = parentMapping.nestedResultMapId;
// 获取association要使用的resultMap信息
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
// 开始进行结果集映射,正好就是挂起的字段和指定的结果集进行映射
handleResultSet(rsw, resultMap, null, parentMapping);
}
// 继续获取下一个结果集,将所有结果集都处理完
rsw = getNextResultSet(stmt);
// 处理结果集后的清理上一次的缓存操作
cleanUpAfterHandlingResultSet();
// 处理的结果集数量+1
resultSetCount++;
}
}
// 处理单结果集的情况,如果是单结果集,将结果集对象的第一个元素返回,如果存在多个结果集,将所有处理的结果集对象返回
return collapseSingleResultList(multipleResults);
}
// 处理游标的多结果集
@Override
public <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException {
// 获取第一个结果集对象
ResultSetWrapper rsw = this.getFirstResultSet(stmt);
// 获取该映射语句的所有结果集映射
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
// 结果集映射的个数
int resultMapCount = resultMaps.size();
// 如果存在结果集的情况下,必须需要resultMap,否则抛出异常
this.validateResultMapsCount(rsw, resultMapCount);
// 并且结果集对象必须只有一个
if (resultMapCount != 1) {
throw new ExecutorException("Cursor results cannot be mapped to multiple resultMaps");
}
// 获取到resultMap信息
ResultMap resultMap = resultMaps.get(0);
// 包装成默认的游标对象返回
return new DefaultCursor<>(this, resultMap, rsw, rowBounds);
}
// 处理存储过程的输出参数
public void handleRefCursorOutputParameter(ResultSet rs, ParameterMapping parameterMapping, MetaObject metaParam) throws SQLException {
if (rs == null) {
return;
}
// 获取引用的resultMap
String resultMapId = parameterMapping.getResultMapId();
// 获取字段映射信息
ResultMap resultMap = configuration.getResultMap(resultMapId);
// 包装ResultSet
ResultSetWrapper rsw = new ResultSetWrapper(rs, configuration);
// 如果不存在自定义的结果处理器
if (this.resultHandler == null) {
// 创建默认的结果处理器
ResultHandler resultHandler = new ResultHandler.DefaultResultHandler(objectFactory);
// 映射每一个行记录到目标结果对象中
// 进行结果映射
handleRowValues(rsw, resultMap, resultHandler, new RowBounds(), null);
// 将结果处理器中处理的结果设置到参数对象中
metaParam.setValue(parameterMapping.getProperty(), resultHandler.getResultList());
}
// 如果存在自定义的结果处理器
else {
// 映射每一个行记录到目标结果对象中
// 进行结果映射
handleRowValues(rsw, resultMap, resultHandler, new RowBounds(), null);
}
// 关闭结果集
closeResultSet(rs);
}
// 处理存储过程输出参数
@Override
public void handleOutputParameters(CallableStatement cs) throws SQLException {
// 获取参数对象
Object parameterObject = parameterHandler.getParameterObject();
MetaObject metaParam = configuration.newMetaObject(parameterObject);
// 获取参数映射信息
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
// 遍历所有的参数映射信息
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
// 如果参数模式是OUT或者INOUT(只有存储过程才会是这两个值)
if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {
// 列参数映射的java类型是否是ResultSet
if (ResultSet.class.equals(parameterMapping.getJavaType())) {
// 处理存储过程的输出参数
handleRefCursorOutputParameter((ResultSet) cs.getObject(i + 1), parameterMapping, metaParam);
} else {
// 如果java类型是其他类型,获取该映射信息中设置的类型转换器
TypeHandler<?> typeHandler = parameterMapping.getTypeHandler();
// 使用类型转换器将该列的值进行转换,设置到参数对象中
metaParam.setValue(parameterMapping.getProperty(), typeHandler.getResult(cs, i + 1));
}
}
}
}
// 处理结果集后的清理上一次的缓存操作
public void cleanUpAfterHandlingResultSet() {
nestedResultObjects.clear();
}
public List<Object> collapseSingleResultList(List<Object> multipleResults) {
return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
}
/**
* 处理结果集的映射关系
* <pre>
* @param rsw 结果集对象
* @param resultMap 结果集需要使用的resultMap
* @param multipleResults 保存多个结果集的集和
* @param parentMapping 其他情况都为null,只有在多结果集的情况下才会出现值,因为多结果集中可能出现正在映射的列指定了要从某个结果集中获取数据进行映射,例如下面这种情况
* 因为处理结果集的时候是一一处理,当处理blogs结果集的时候,存在author字段,该字段指定了要从authors结果集中获取数据进行映射
* 那么这种情况,正在处理的blogs结果集肯定是无法完成的,只能等到处理authors结果集的时候,再将author这个映射列信息拿出来进行映射
* <select id="selectBlog" resultSets="blogs,authors" resultMap="blogResult" statementType="CALLABLE">
* {call getBlogsAndAuthors(#{id,jdbcType=INTEGER,mode=IN})}
* </select>
* <resultMap id="blogResult" type="Blog">
* <id property="id" column="id" />
* <association property="author" javaType="Author" resultSet="authors" resultMap="authorMap" column="author_id" foreignColumn="id">
* <id property="id" column="id"/>
* </association>
* </resultMap>
* </pre>
*/
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
// 如果parentMapping不为空,只会存在多结果集的情况下,表示上一个结果集(A)处理的时候,有映射字段标记了需要是有当前结果集(B)进行数据映射
// 例如: 首先处理的a结果集,但是a结果集的映射信息中存在<association property="author" javaType="Author" resultSet="b" resultMap="a" column="author_id" foreignColumn="id"/>
// 而当前的ResultSet则是b结果集,所以要将之前挂起的author的字段信息拿出来在当前b结果集中进行映射
if (parentMapping != null) {
// 处理被挂起的字段的映射信息
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
}
// 单个结果集的情况
// 没有指定自定义的结果处理器,注意: 如果方法参数中存在结果处理器,那么方法必须是返回void才会生效,否则resultHandler参数也是为null
else if (resultHandler == null) {
// 创建默认的结果处理器
ResultHandler.DefaultResultHandler defaultResultHandler = new ResultHandler.DefaultResultHandler(objectFactory);
// 处理结果集映射
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
// 保存处理好的结果,最终要返回给用户
multipleResults.add(defaultResultHandler.getResultList());
}
// 单个结果集的情况
// 注意: 如果方法参数中存在结果处理器,那么方法必须是返回void才会生效,否则resultHandler参数也是为null
// 如果指定了结果处理器,那么只会将每一行结果交给处理器处理,最终并不会将结果返回给用户
else {
// 处理结果集映射
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
// 关闭结果集
closeResultSet(rsw.getResultSet());
}
// 关闭结果集
public void closeResultSet(ResultSet rs) {
if (rs != null) {
rs.close();
}
}
// 映射每一个行记录到目标结果对象中
// 进行结果映射
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
// 当前映射语句的resultMap中是否存在嵌套的resultMap
// 在resultMap标签中,如果具有"association", "collection", "case"其中的一个,那么就是嵌套的resultMap,因为在这些标签内部,也可以填写与resultMap完全一样的映射关系
if (resultMap.hasNestedResultMaps()) {
// 校验分页的边界问题
ensureNoRowBounds();
// 校验带有嵌套结果映射的映射语句不能与自定义ResultHandler一起使用
checkResultHandler();
// 通过行数据填充嵌套的resultMap
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
// 如果是一个简单的映射类型,单独的resultMap或者resultType
else {
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
// 如果是一个简单的映射类型,单独的resultMap或者resultType
public void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
// 创建结果上下文
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
// 获取结果集对象
ResultSet resultSet = rsw.getResultSet();
// 分页跳到指定的行,处理offset
skipRows(resultSet, rowBounds);
// 是否需要继续处理结果还是需要停止处理(上下文对象没有设置停止处理结果集,并且处理的结果数量没有达到分页限制)
// 并且结果集未关闭且有值的情况才会继续处理
while (this.shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
// 处理discriminator,因此如果存在discriminator,则要使用discriminator中value对应的那个resultMap,那个resultMap对原始的resultMap和case的映射列进行了合并
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
// 创建该resultMap的返回类型对象并且使用当前结果集行的值进行映射
Object rowValue = this.getRowValue(rsw, discriminatedResultMap, null);
// 将该行的值保存到上下文对象中,通过结果执行器处理,或者存在多结果集的情况下是将当前行的结果设置到上一个结果集被挂起的对象属性中
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
/**
* 将该行的值保存到上下文对象中,通过结果执行器处理,或者存在多结果集的情况下是将当前行的结果设置到上一个结果集被挂起的对象属性中
* <pre>
* @param resultHandler 行结果处理器
* @param resultContext 结果上下文
* @param rowValue 当前行的值
* @param parentMapping 其他情况都为null,只有在多结果集的情况下才会出现值,因为多结果集中可能出现正在映射的列指定了要从某个结果集中获取数据进行映射,例如下面这种情况
* 因为处理结果集的时候是一一处理,当处理blogs结果集的时候,存在author字段,该字段指定了要从authors结果集中获取数据进行映射
* 那么这种情况,正在处理的blogs结果集肯定是无法完成的,只能等到处理authors结果集的时候,再将author这个映射列信息拿出来进行映射
* <select id="selectBlog" resultSets="blogs,authors" resultMap="blogResult" statementType="CALLABLE">
* {call getBlogsAndAuthors(#{id,jdbcType=INTEGER,mode=IN})}
* </select>
* <resultMap id="blogResult" type="Blog">
* <id property="id" column="id" />
* <association property="author" javaType="Author" resultSet="authors" resultMap="authorMap" column="author_id" foreignColumn="id">
* <id property="id" column="id"/>
* </association>
* </resultMap>
* @param rs 正在处理的结果集对象
* @throws SQLException
* </pre>
*/
public void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
// 如果parentMapping不为空,只会存在多结果集的情况下,表示上一个结果集(A)处理的时候,有映射字段标记了需要是有当前结果集(B)进行数据映射
// 例如: 首先处理的a结果集,但是a结果集的映射信息中存在<association property="author" javaType="Author" resultSet="b" resultMap="a" column="author_id" foreignColumn="id"/>
// 而当前的ResultSet则是b结果集,所以要将之前挂起的author的字段信息拿出来在当前b结果集中进行映射
if (parentMapping != null) {
// 将挂起的映射信息拿出来到当前结果集中进行映射
linkToParents(rs, parentMapping, rowValue);
}
// 如果不存在多结果集的情况下,直接使用结果处理器处理结果
else {
callResultHandler(resultHandler, resultContext, rowValue);
}
}
// 处理嵌套的结果映射
public void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
// 创建默认的结果上下文对象
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
// 获取结果集对象
ResultSet resultSet = rsw.getResultSet();
// 分页跳到指定的行,处理offset
skipRows(resultSet, rowBounds);
// 前一行的的值,用于处理多结果集
Object rowValue = previousRowValue;
// 是否需要继续处理结果还是需要停止处理(上下文对象没有设置停止处理结果集,并且处理的结果数量没有达到分页限制)
// 并且结果集未关闭且有值的情况才会继续处理
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
// 处理Discriminator类型的嵌套resultMap,按照分支来决定使用的ResultMap
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
// 创建缓存这一行结果的缓存KEY
CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);
// 通过该缓存的key来确定这些要映射的列是否在行记录中有重复的记录
// 如果有,则直接获取出来,而不需要去创建新对象
Object partialObject = nestedResultObjects.get(rowKey);
// 该属性决定是否使用查询结果进行映射
// 如果为true: 那么结果映射的时候会按照sql的查询列的顺序进行一一映射
// 如果为false: 那么就会按照resultMap设置的列的顺序进行映射
if (mappedStatement.resultOrdered) {
// 如果没有缓存嵌套结果,并且上一行存在值
if (partialObject == null && rowValue != null) {
// 清空嵌套结果对象
nestedResultObjects.clear();
// 将该行的值保存到上下文对象中,通过结果执行器处理,或者存在多结果集的情况下是将当前行的结果设置到上一个结果集被挂起的对象属性中
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
// 从嵌套结果映射的行获取值
rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
} else {
// 从嵌套结果映射的行获取值
rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
// 如果未缓存了这一行的记录的子查询结果
if (partialObject == null) {
// 将该行的值保存到上下文对象中,通过结果执行器处理,或者存在多结果集的情况下是将当前行的结果设置到上一个结果集被挂起的对象属性中
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
}
// 如果当前列的值不为空,并且需要按照顺序映射,并且没有中断结果处理
if (rowValue != null && mappedStatement.isResultOrdered() && shouldProcessMoreRows(resultContext, rowBounds)) {
// 将该行的值保存到上下文对象中,通过结果执行器处理,或者存在多结果集的情况下是将当前行的结果设置到上一个结果集被挂起的对象属性中
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
// 将上一行的值置为空
previousRowValue = null;
}
// 如果不需要有序进行映射,或者中断处理结果了,则将当前行数据保存起来,保存到previousRowValue中
else if (rowValue != null) {
previousRowValue = rowValue;
}
}
// 从单行记录中创建结果对象,会解析属性的嵌套映射,使用递归操作
public Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, String columnPrefix, Object partialObject) throws SQLException {
// 获取resultMapId
String resultMapId = resultMap.getId();
// 已经缓存的行的值,如果没有缓存,则为null
Object rowValue = partialObject;
// 如果已经缓存了当前行的对象,表示处理过类似的数据
if (rowValue != null) {
// 封装为元数据信息
MetaObject metaObject = configuration.newMetaObject(rowValue);
// 将当前行的结果缓存起来,key为resultMapId
// 因为接下来要执行嵌套字段映射,需要用到,因为嵌套映射就是子属性映射,所以要将当前父对象缓存,在真正执行子属性映射的时候,可以映射完成的结果设置到父对象中
// 每一个复杂属性association,collection都会对应一个resultMap,因此使用resultMapId作为缓存Key
// 缓存父对象主要是为了解决循环依赖,例如在User中包含Role,在Role中又包含User
putAncestor(rowValue, resultMapId);
// 执行嵌套的resultMap字段映射
applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
// 处理了完了该属性的嵌套映射,将缓存的该resultMap对应的对象删除
ancestorObjects.remove(resultMapId);
}
// 如果没有缓存结果对象,则需要创建这一行的结果对象
else {
// 保存所有懒加载的属性的集合
ResultLoaderMap lazyLoader = new ResultLoaderMap();
// 创建当前行的结果对象
rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
// 如果创建了当前行对应的结果对象,但是不存在这个结果对象的类型转换器
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
// 封装成结果对象的元数据信息
MetaObject metaObject = configuration.newMetaObject(rowValue);
// 是否找到了可以映射的值
boolean foundValues = this.useConstructorMappings;
// 是否需要进行自动映射
if (shouldApplyAutomaticMappings(resultMap, true)) {
// 执行自动映射操作,将未能映射上的列或者没有设置映射关系的列尝试进行自动映射
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
// 再执行在resultMap中标记的属性映射
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
// 将当前行的结果缓存起来,key为resultMapId
// 因为接下来要执行嵌套字段映射,需要用到,因为嵌套映射就是子属性映射,所以要将当前父对象缓存,在真正执行子属性映射的时候,可以映射完成的结果设置到父对象中
// 每一个复杂属性association,collection都会对应一个resultMap,因此使用resultMapId作为缓存Key
// 缓存父对象主要是为了解决循环依赖,例如在User中包含Role,在Role中又包含User
putAncestor(rowValue, resultMapId);
// 执行当前创建的结果对象中子属性的的嵌套映射
foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
// 处理了完了该属性的嵌套映射,将缓存的该resultMap对应的对象删除
ancestorObjects.remove(resultMapId);
// 存在懒加载的属性,或者找到了属性映射的值
foundValues = lazyLoader.size() > 0 || foundValues;
// 返回这一行结果集处理好的结果对象
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
// 如果存在合并的Key,将当前正在处理的resultMap的值缓存起来
// 正常情况下,同一个resultMap中的几乎所有的对象都会被缓存
if (combinedKey != CacheKey.NULL_CACHE_KEY) {
// 将当前行的结果进行缓存起来
nestedResultObjects.put(combinedKey, rowValue);
}
}
return rowValue;
}
// 创建该resultMap的返回类型对象并且使用当前结果集行的值进行映射
public Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
// 创建保存所有懒加载的属性的集合
ResultLoaderMap lazyLoader = new ResultLoaderMap();
// 创建当前行的结果对象
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
// 如果创建了当前行对应的结果对象,但是不存在这个结果对象的类型转换器
if (rowValue != null && !this.hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
// 封装成结果对象的元数据信息
MetaObject metaObject = configuration.newMetaObject(rowValue);
// 是否找到了可以映射的值
boolean foundValues = this.useConstructorMappings;
// 是否需要进行自动映射
if (this.shouldApplyAutomaticMappings(resultMap, false)) {
// 执行自动映射操作,将未能映射上的列或者没有设置映射关系的列尝试进行自动映射
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
// 再执行在resultMap中标记的属性映射
foundValues = this.applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
// 存在懒加载的属性,或者找到了属性映射的值
foundValues = lazyLoader.size() > 0 || foundValues;
// 返回这一行结果集处理好的结果对象
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
// 执行嵌套的结果字段映射
public boolean applyNestedResultMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String parentPrefix, CacheKey parentRowKey, boolean newObject) {
boolean foundValues = false;
// 遍历所有的字段映射
for (ResultMapping resultMapping : resultMap.propertyResultMappings) {
// 获取嵌套的resultMapId
// 获取在association或colllection指定的resultMap
// 因为只有association或者colllection这两个标签需要进行嵌套映射,其他的普通字段都没有
String nestedResultMapId = resultMapping.nestedResultMapId;
/**
* 如果存在嵌套的resultMap并且没有指定该属性映射需要从指定结果集获取结果进行映射才需要处理
* 其他情况,例如,简单字段映射,或者给association或colllection属性设置了resultSets在上面
* {@link ResultSetHandler.DefaultResultSetHandler#getPropertyMappingValue}中已经处理过,所以直接忽略
*/
if (nestedResultMapId != null && resultMapping.getResultSet() == null) {
// 获取列前缀
String columnPrefix = this.getColumnPrefix(parentPrefix, resultMapping);
// 获取嵌套的resultMap信息
ResultMap nestedResultMap = getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix);
// 处理嵌套查询中的循环引用,有前缀不会处理循环依赖
if (resultMapping.getColumnPrefix() == null) {
// 获取父对象,因为现在是处理嵌套属性(也就是子属性),那么父对象一定创建了
// 例如User对象包含Role属性,则现在处理Role对象的映射,那么User对象已经创建了
// 缓存父对象主要是为了解决循环依赖,例如在User中包含Role,在Role中又包含User
// 在执行当前applyNestedResultMappings方法之前,就已经将父对象与它对应的resultMapId缓存了
/**
* <pre>
* 案例 {@link ResultSetHandler.DefaultResultSetHandler#putAncestor}
* {@link ResultSetHandler.DefaultResultSetHandler#getRowValue}
* <resultMap id="allResultMap" type="Users">
* <association property="roles" javaType="Roles" resultMap="roleResultMap">
* <id property="id" column="id"/>
* 此时,Roles对象中,又引用了allResultMap,也就是User对象这个ResultMap
* <association property="users" javaType="Users" resultMap="allResultMap"/>
* </association>
* </resultMap>
* </pre>
*/
// 通过以上案例,就可以发现,User映射Role,而Role又映射User,此时映射的User使用的resultMap是最外层User的resultMap
// 而User的resultMap在执行当前方法之前,就已经将User的resultMapId->User对象缓存到ancestorObjects对象中了
// 因此,这个就能获取到之前缓存的User对象,然后直接设置到Role对象中,从而解决了循环依赖
Object ancestorObject = ancestorObjects.get(nestedResultMapId);
// 如果存在缓存的父对象才表示出现了循环依赖,才有必要进行设置值,否则就走正常的嵌套映射,创建该嵌套的resultMap的结果对象并进行映射
if (ancestorObject != null) {
// 如果是新创建的对象
if (newObject) {
// 将该父对象设置到结果对象中
// 将指定属性的值设置到结果对象中
linkObjects(metaObject, resultMapping, ancestorObject);
}
continue;
}
}
// 正式
// 如果存在列前缀,就要拼接该列前缀
// 创建这一行结果的唯一的缓存结果的KEY
CacheKey rowKey = createRowKey(nestedResultMap, rsw, columnPrefix);
// 合并key,因为出现嵌套映射的时候,需要对父对象数据和子对象数据进行合并来确定是否存在缓存的记录
CacheKey combinedKey = combineKeys(rowKey, parentRowKey);
// 嵌套结果对象,只要是处理过的嵌套的属性,都会缓存到nestedResultObjects中
Object rowValue = nestedResultObjects.get(combinedKey);
// 是否存在值缓存的值
boolean knownValue = rowValue != null;
// 初始化集合类型的字段值
this.instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject); // mandatory
// 是否在任何一个空列中存在值,也就是只要列存在数据,就可以
if (this.anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw)) {
// 递归设置子属性
rowValue = getRowValue(rsw, nestedResultMap, combinedKey, columnPrefix, rowValue);
// 在递归设置属性之前,没有值但是递归之后又存在值,表示在递归操作中,处理好了这一条记录
if (rowValue != null && !knownValue) {
// 将嵌套属性的值设置到结果对象中
linkObjects(metaObject, resultMapping, rowValue);
// 标记找到了值
foundValues = true;
}
}
}
}
return foundValues;
}
// 调用结果处理器的执行逻辑
public void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
resultContext.nextResultObject(rowValue);
((ResultHandler<Object>) resultHandler).handleResult(resultContext);
}
// 执行属性的映射
public boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
// 获取所有在resultMap中的column列名与结果集列名匹配的所有列名
List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
// 是否找到了映射的值
boolean foundValues = false;
// 获取所有的属性映射关系
List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
// 遍历所有的映射关系
for (ResultMapping propertyMapping : propertyMappings) {
// 拼接前缀
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
// 如果存在嵌套映射
if (propertyMapping.getNestedResultMapId() != null) {
// 用户在嵌套属性中填写了列名,忽略,因为嵌套属性不是通过列名映射,要单独处理
column = null;
}
// 1. 会处理设置了column = {prop: column,prop: column}
// 2. 会处理设置了column属性 column ="id" 并且该结果集的列名与resultMap中标注的column一致
// 3. 会处理设置了column属性 column ="id" 并且属性映射中设置了使用指定的结果集
if (propertyMapping.isCompositeResult() || column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)) || propertyMapping.resultSet() != null) {
// 获取该属性映射的值,会处理嵌套查询和多结果集处理的值
Object value = this.getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
// 获取属性名
String property = propertyMapping.getProperty();
// 如果不存在属性名,不处理
if (property == null) {
continue;
}
// 如果值被标记为了延迟加载
// 出现了循环依赖,或者设置了懒加载
if (value == DEFERRED) {
// 标记找到了列值
foundValues = true;
continue;
}
// 如果未标记延迟加载,是正常映射的值
if (value != null) {
// 标记找到了列值
foundValues = true;
}
// 如果找到了正常映射的列值,将属性值设置到结果对象中
if (value != null || configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive()) {
metaObject.setValue(property, value);
}
}
}
// 返回是否找到了该列的值
return foundValues;
}
// 获取属性映射的值
public Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
// 是否存在嵌套查询,处理复杂属性(JavaBean)映射的情况才可能会有
if (propertyMapping.getNestedQueryId() != null) {
// 通过嵌套查询获取映射值
return this.getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
}
// 不存在嵌套查询
// 如果指定了要从某个结果集中获取值,只能在association,collection使用
// 也就是这个JavaBean的结果要从指定的结果集中获取数据进行映射
if (propertyMapping.getResultSet() != null) {
/**
* 如果指定了要从某个结果集中获取值,只能在association,collection使用
* 也就是这个JavaBean的结果要从指定的结果集中获取数据进行映射
* 例如,某个查询设置了结果会产生多个结果集,而映射关系的时候,association设置了该属性从某一个结果集中获取数据
* 处理结果的时候,是一个结果集一个结果集处理,如果指定的那个结果集还未处理到,那么要先保存这种映射关系,到处理它指定的那个结果集的时候就拿出来处理
*/
this.addPendingChildRelation(rs, metaResultObject, propertyMapping);
// 返回一个标识,该映射关系需要延迟处理
return DEFERRED;
}
// 未指定使用的结果集,直接通过类型转换器将当前列值进行转换
else {
// 获取映射指定的类型转换器
TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
// 拼接前缀
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
// 使用类型转换器获取列值
return typeHandler.getResult(rs, column);
}
}
// 获取嵌套查询结果映射的值,如果是延迟加载或者懒加载,都会返回延迟加载的标识
public Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
// 获取嵌套查询ID
String nestedQueryId = propertyMapping.getNestedQueryId();
String property = propertyMapping.getProperty();
// 获取嵌套查询语句
MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
// 获取参数类型
Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
// 根据嵌套查询设置的column列名,从结果集中获取列值设置到参数对象中,供给嵌套查询对象使用
Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, propertyMapping, nestedQueryParameterType, columnPrefix);
Object value = null;
// 如果结果集列中的值为null,那么nestedQueryParameterObject就会为空,此时不会执行嵌套查询
if (nestedQueryParameterObject != null) {
// 获取嵌套查询的SQL信息
BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
// 创建缓存KEY
CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
Class<?> targetType = propertyMapping.getJavaType();
// 是否存在该查询的缓存结果,存在缓存结果
if (executor.isCached(nestedQuery, key)) {
// 使用延迟加载操作,该操作时使用嵌套查询解决循环依赖问题
// 当缓存数据中结果对象,则直接设置到目标结果对象中
// 如果缓存数据中不存在结果对象,此时将相关信息保存到延迟加载队列中
// 等主查询结束之后,统一将队列中所有的延迟加载的属性从缓存中获取数据进行属性设置
executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
// 返回延迟加载的标识
value = DEFERRED;
}
// 如果没有缓存该嵌套查询的结果
else {
// 创建结果加载器
ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
// 属性是否是懒加载
if (propertyMapping.isLazy()) {
// 将懒加载属性的详细信息保存到懒加载的属性集合中
lazyLoader.addLoader(property, metaResultObject, resultLoader);
// 返回延迟加载的标识
value = DEFERRED;
}
// 如果没有设置为懒加载,直接加载结果,执行嵌套查询操作
else {
value = resultLoader.loadResult();
}
}
}
return value;
}
// 创建该resultMap的返回类型对象并且使用当前结果集行的值进行映射
public Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
// 重置之前的映射结果
this.useConstructorMappings = false;
// 构造函数参数类型
List<Class<?>> constructorArgTypes = new ArrayList<>();
// 构造函数参数对值
List<Object> constructorArgs = new ArrayList<>();
// 根据不同的条件创建目标对象,例如构造映射,返回接口等各种情况
Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
// 如果创建成功了对象,表示映射成功了
if (resultObject != null &&
// 如果创建结果对象成功之后
// 确定是否存在该结果对象的类型转换器,如果找到了结果对象的类型转换器,即使需要懒加载,也不会进行懒加载操作
// 如果不存在的情况下,要判断resultMap的映射字段中是否存在嵌套查询并且设置了懒加载的属性
// 如果设置嵌套查询并且设置了懒加载,要为该结果对象创建代理对象,使用代理对象执行懒加载操作
!this.hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
// 获取所有的属性映射
List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
// 遍历所有的属性映射
for (ResultMapping propertyMapping : propertyMappings) {
// 如果该映射为嵌套查询,并且设置为懒加载
// 此时要为结果对象创建代理对象,因为懒加载的情况下,需要在调用的时候才会执行SQL
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
resultObject = configuration.proxyFactory.createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
break;
}
}
}
this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty();
return resultObject;
}
// 根据不同的条件创建目标对象,例如构造映射,返回接口等各种情况
public Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) throws SQLException {
// 获取resultMap的返回类型
Class<?> resultType = resultMap.getType();
// 包装成元数据类型
MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
// 获取构造参数映射
List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
// 存在该结果对象的类型转换器
if (this.hasTypeHandlerForResultObject(rsw, resultType)) {
// 直接使用类型转换器提供的对象
return this.createPrimitiveResultObject(rsw, resultMap, columnPrefix);
}
// 如果没有提供该结果对象的类型转换器
// 根据构造映射提供列名和列值找到对象的有参构造创建对象
if (!constructorMappings.isEmpty()) {
return this.createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
}
// 如果是接口或者有默认的构造函数,直接使用对象工厂进行创建对象
if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
return this.objectFactory.create(resultType);
}
// 如果还是不满足,判断是否需要进行自动映射
if (shouldApplyAutomaticMappings(resultMap, false)) {
// 找到所有的构造方法进行查找,找到可以进行映射的构造方法,如果没有找到,抛出异常
// 如果找到可用的构造方法,使用该构造方法进行映射
return this.createByConstructorSignature(rsw, resultMap, columnPrefix, resultType, constructorArgTypes, constructorArgs);
}
// 其他情况,无法创建对象,抛出异常
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
// 是否存在该结果对象的类型转换器
public boolean hasTypeHandlerForResultObject(ResultSetWrapper rsw, Class<?> resultType) {
// 如果结果只有一列
if (rsw.getColumnNames().size() == 1) {
// 判断是否有指定java类型并且指定JDBC类型的类型转换器
return typeHandlerRegistry.hasTypeHandler(resultType, rsw.getJdbcType(rsw.getColumnNames().get(0)));
}
// 判断是否有指定java类型的类型转换器
return typeHandlerRegistry.hasTypeHandler(resultType);
}
// 使用类型转换器提供的对象
public Object createPrimitiveResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
// 获取resultMap最终要封装的java类型
Class<?> resultType = resultMap.getType();
String columnName;
// 如果该ResultMap存在字段映射
// 我们只需要获取第一个列名就可以,为什么?
// 首先,如果要返回的结果对象是一个简单类型,那么使用一个字段就足以得出结果对象
// 第二: 如果返回的对象是一个复杂类型,但是又存在类型转换器,而类型转换器通过某一个列名就能得到该结果对象,那么就算执行所有列名,还是只能返回一个结果对象
// 换句话说,当前只需要创建一个对象,而类型转换器就已经会提供这个对象,就算又多个列名,那该使用那个对象呢?,所以不管任何情况,有类型转换器的情况就直接执行一次就行
if (!resultMap.getResultMappings().isEmpty()) {
// 获取到所有的结果映射
List<ResultMapping> resultMappingList = resultMap.getResultMappings();
// 获取到第一个映射信息
ResultMapping mapping = resultMappingList.get(0);
// 拼接列名前缀
columnName = prependPrefix(mapping.getColumn(), columnPrefix);
} else {
// 如果不存在字段映射,直接获取第一个列名
columnName = rsw.getColumnNames().get(0);
}
// 获取该列的类型转换器
TypeHandler<?> typeHandler = rsw.getTypeHandler(resultType, columnName);
// 通过类型转换器创建对象
return typeHandler.getResult(rsw.getResultSet(), columnName);
}
// 根据构造映射提供列名和列值找到对象的有参构造创建对象
public Object createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType, List<ResultMapping> constructorMappings, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) {
boolean foundValues = false;
// 遍历所有构造映射信息
for (ResultMapping constructorMapping : constructorMappings) {
Class<?> parameterType = constructorMapping.getJavaType();
String column = constructorMapping.getColumn();
Object value;
// 如果存在嵌套查询
if (constructorMapping.getNestedQueryId() != null) {
// 获取嵌套查询的构造参数值
value = getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix);
}
// 如果不存在嵌套查询,但是存在嵌套映射
else if (constructorMapping.getNestedResultMapId() != null) {
// 获取到嵌套映射信息
ResultMap resultMap = configuration.getResultMap(constructorMapping.getNestedResultMapId());
// 递归处理该映射信息
value = getRowValue(rsw, resultMap, getColumnPrefix(columnPrefix, constructorMapping));
}
// 不存在嵌套查询和嵌套结果集对象
else {
// 获取构造参数类型对应的类型转换器
TypeHandler<?> typeHandler = constructorMapping.getTypeHandler();
// 通过类型转换器获取该列的结果
value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(column, columnPrefix));
}
// 保存构造参数类型
constructorArgTypes.add(parameterType);
// 保存构造函数值
constructorArgs.add(value);
// 是否找到了构造参数
foundValues = value != null || foundValues;
}
// 根据找到的构造参数类型和构造参数值对应的有参构造创建对象
return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
}
// 获取嵌套查询的构造函数值
public Object getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix) throws SQLException {
// 获取嵌套查询ID
String nestedQueryId = constructorMapping.getNestedQueryId();
// 获取该嵌套查询的statement映射语句信息
MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
// 获取嵌套查询的参数类型
Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
// 根据嵌套查询设置的column列名,从结果集中获取列值设置到参数对象中,供给嵌套查询对象使用
Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, constructorMapping, nestedQueryParameterType, columnPrefix);
Object value = null;
// 如果结果集列中的值为null,那么nestedQueryParameterObject就会为空,此时不会执行嵌套查询
if (nestedQueryParameterObject != null) {
// 获取嵌套查询的SQL信息
BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
// 创建缓存KEY
CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
Class<?> targetType = constructorMapping.getJavaType();
// 使用加载器去加载属性
ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
// 执行嵌套查询操作
value = resultLoader.loadResult();
}
return value;
}
// 根据嵌套查询设置的column列名,从结果集中获取列值设置到参数对象中,供给嵌套查询对象使用
public Object prepareParameterForNestedQuery(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) throws SQLException {
// 是否是符合字段{prop=col,prop=col}这中参数信息
if (resultMapping.isCompositeResult()) {
// 处理复合属性的参数对象,将列中的对应的列值设置到参数对象中
return prepareCompositeKeyParameter(rs, resultMapping, parameterType, columnPrefix);
}
// 通过参数类型获取对应的类型转换器,如果不存在该参数的类型转换器,使用万能的类型转换器从结果集对象中获取需要的值设置到结果参数对象中
return prepareSimpleKeyParameter(rs, resultMapping, parameterType, columnPrefix);
}
// 通过参数类型获取对应的类型转换器,如果不存在该参数的类型转换器,使用万能的类型转换器从结果集对象中获取需要的值设置到结果参数对象中
public Object prepareSimpleKeyParameter(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) throws SQLException {
TypeHandler<?> typeHandler;
// 如果存在该参数类型的类型转换器
if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
// 获取类型转换器
typeHandler = typeHandlerRegistry.getTypeHandler(parameterType);
} else {
// 获取未知类型的类型转换器
// 该对象是万能的,如果没有设置对应的java类型获取jdbc类型,它都会进行自动推断,如果没有根据java类型或者jdbc类型推断成功,则使用万能的ObjectTypeHandler
typeHandler = typeHandlerRegistry.getUnknownTypeHandler();
}
// 通过类型转换器获取生成结果对象
return typeHandler.getResult(rs, prependPrefix(resultMapping.getColumn(), columnPrefix));
}
// 处理复合属性的参数对象,将列中的对应的列值设置到参数对象中
public Object prepareCompositeKeyParameter(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) throws SQLException {
// 根据参数类型实例化参数对象,如果指定了参数类型,那么创建参数类型的对象,否则都创建Map
Object parameterObject = instantiateParameterObject(parameterType);
// 封装为参数的元数据信息
MetaObject metaObject = configuration.newMetaObject(parameterObject);
boolean foundValues = false;
// 获取所有的复合映射列信息
for (ResultMapping innerResultMapping : resultMapping.composites) {
// 获取属性的类型
Class<?> propType = metaObject.getSetterType(innerResultMapping.getProperty());
// 获取属性类型对应的类型转换器
TypeHandler<?> typeHandler = typeHandlerRegistry.getTypeHandler(propType);
// 通过类型转换器获取到对应的属性值
Object propValue = typeHandler.getResult(rs, prependPrefix(innerResultMapping.getColumn(), columnPrefix));
// 如果获取到了属性值
if (propValue != null) {
// 将该属性值设置到参数对象中
metaObject.setValue(innerResultMapping.getProperty(), propValue);
foundValues = true;
}
}
// 返回处理好的参数对象
return foundValues ? parameterObject : null;
}
// 根据参数类型实例化参数对象,如果指定了参数类型,那么创建参数类型的对象,否则都创建Map
public Object instantiateParameterObject(Class<?> parameterType) {
if (parameterType == null) {
return new HashMap<>();
}
if (ParamMap.class.equals(parameterType)) {
return new HashMap<>();
} else {
return objectFactory.create(parameterType);
}
}
/**
* 只会出现在多结果集的情况下,将挂起的映射信息拿出来到当前结果集中进行映射
* {@link ResultSetHandler.DefaultResultSetHandler#storeObject}
*/
public void linkToParents(ResultSet rs, ResultMapping parentMapping, Object rowValue) throws SQLException {
// 创建存在多个结果的缓存Key
CacheKey parentKey = this.createKeyForMultipleResults(rs, parentMapping, parentMapping.getColumn(), parentMapping.getForeignColumn());
// 缓存指定了要使用某一个结果集进行结果映射但是该结果集还未被处理的那些字段信息
// 例如: 存在多个结果集A1,A2,处理A1结果集的时候,有resultMap中的字段指定了要使用A2的结果集中的字段进行映射
// 此时,会为A1结果集创建一个缓存Key,缓存该结果集挂起(也就是指定了A2结果的)的所有的字段映射信息
List<PendingRelation> parents = pendingRelations.get(parentKey);
// 如果存在挂起的映射列信息
if (parents != null) {
// 遍历所有挂起的映射关系
for (PendingRelation parent : parents) {
// 如果存在挂起的映射字段,并且当前行的值处理好了
if (parent != null && rowValue != null) {
// 将当前行的值设置到被挂起的字段中
linkObjects(parent.metaObject, parent.propertyMapping, rowValue);
}
}
}
}
/**
* 如果指定了要从某个结果集中获取值,只能在association,collection使用
* 也就是这个JavaBean的结果要从指定的结果集中获取数据进行映射
* 例如,某个查询设置了结果会产生多个结果集,而映射关系的时候,association设置了该属性从某一个结果集中获取数据
* <pre>
* <select id="selectBlog" resultSets="blogs,authors" resultMap="blogResult" statementType="CALLABLE">
* {call getBlogsAndAuthors(#{id,jdbcType=INTEGER,mode=IN})}
* </select>
*
* <resultMap id="blogResult" type="Blog">
* <id property="id" column="id" />
* <result property="title" column="title"/>
* <association property="author" javaType="Author" resultSet="authors" resultMap="authorMap" column="author_id" foreignColumn="id">
* <id property="id" column="id"/>
* <result property="username" column="username"/>
* </association>
* </resultMap>
* </pre>
* 处理结果的时候,是一个结果集一个结果集处理,如果指定的那个结果集还未处理到,那么要先保存这种映射关系,到处理它指定的那个结果集的时候就拿出来处理
*/
public void addPendingChildRelation(ResultSet rs, MetaObject metaResultObject, ResultMapping parentMapping) throws SQLException {
// 创建这在处理的结果集的缓存Key
CacheKey cacheKey = this.createKeyForMultipleResults(rs, parentMapping, parentMapping.getColumn(), parentMapping.getColumn());
// 创建延迟加载的映射信息,因为它要从指定结果集中获取数据
PendingRelation deferLoad = new PendingRelation(metaResultObject, parentMapping);
// 保存该延迟加载的列信息
List<PendingRelation> relations = MapUtil.computeIfAbsent(pendingRelations, cacheKey, k -> new ArrayList<>());
relations.add(deferLoad);
// 查询是否缓存了该指定的结果集对应的映射列信息
ResultMapping previous = nextResultMaps.get(parentMapping.getResultSet());
// 如果没有缓存
if (previous == null) {
// 缓存该结果集到该列的映射信息的关系
nextResultMaps.put(parentMapping.getResultSet(), parentMapping);
}
// 如果存在两个不同的属性映射到同一个resultSet,抛出异常
else if (!previous.equals(parentMapping)) {
throw new ExecutorException("Two different properties are mapped to the same resultSet");
}
}
// 创建存在多个结果的缓存Key
public CacheKey createKeyForMultipleResults(ResultSet rs, ResultMapping resultMapping, String names, String columns) throws SQLException {
// 创建缓存Key
CacheKey cacheKey = new CacheKey();
// 添加该字段的映射关系
cacheKey.update(resultMapping);
// 如果存在列名信息和外键信息
if (columns != null && names != null) {
// 使用,分割列名
String[] columnsArray = columns.split(",");
// 使用,分割外键列(可能是外键列,也可能是普通列)
String[] namesArray = names.split(",");
// 遍历所有列
for (int i = 0; i < columnsArray.length; i++) {
// 获取该列的值
Object value = rs.getString(columnsArray[i]);
if (value != null) {
// 添加该列的列名(可能是外键列,也可能是普通列)
cacheKey.update(namesArray[i]);
// 添加该列的值
cacheKey.update(value);
}
}
}
return cacheKey;
}
// 找到所有的构造方法进行查找,找到可以进行映射的构造方法,如果没有找到,抛出异常
// 如果找到可用的构造方法,使用该构造方法进行映射
public Object createByConstructorSignature(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) throws SQLException {
// 执行构造映射
return applyConstructorAutomapping(rsw, resultMap, columnPrefix, resultType, constructorArgTypes, constructorArgs,
// 找到所有的构造方法进行查找,找到可以进行映射的构造方法,如果没有找到,抛出异常
findConstructorForAutomapping(resultType, rsw).orElseThrow(() -> new ExecutorException("No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames())));
}
// 查找构造函数来做自动映射操作
public Optional<Constructor<?>> findConstructorForAutomapping(Class<?> resultType, ResultSetWrapper rsw) {
// 获取所有的构造函数
Constructor<?>[] constructors = resultType.getDeclaredConstructors();
// 如果只有一个,返回
if (constructors.length == 1) {
return Optional.of(constructors[0]);
}
// 如果存在多个,找构造方法中是否存在@AutomapConstructor注解,如果在多个构造方法中标注,抛出异常
Optional<Constructor<?>> annotated = Arrays.stream(constructors)
.filter(x -> x.isAnnotationPresent(AutomapConstructor.class)).reduce((x, y) -> {
throw new ExecutorException("@AutomapConstructor should be used in only one constructor.");
});
// 如果找到唯一标注了@AutomapConstructor注解的构造方法,返回该构造函数
if (annotated.isPresent()) {
return annotated;
}
// 如果不存在@AutomapConstructor注解但是又配置了使用构造参数名称进行映射,抛出异常
if (configuration.argNameBasedConstructorAutoMapping) {
throw new ExecutorException(MessageFormat.format("'argNameBasedConstructorAutoMapping' is enabled and the class ''{0}'' has multiple constructors, so @AutomapConstructor must be added to one of the constructors.", resultType.getName()));
}
// 如果没有开启使用参数名进行映射,那么根据结果集中的JDBC类型匹配
// 通过构造参数类型去结果集中一一进行类型匹配,构造参数类型和列的jdbc类型全部匹配上才能用,否则不可用
return Arrays.stream(constructors).filter(x -> findUsableConstructorByArgTypes(x, rsw.getJdbcTypes())).findAny();
}
// 通过构造参数类型去结果集中一一进行类型匹配,构造参数类型和列的jdbc类型全部匹配上才能用,否则不可用
public boolean findUsableConstructorByArgTypes(Constructor<?> constructor, List<JdbcType> jdbcTypes) {
// 获取所有构造参数类型
Class<?>[] parameterTypes = constructor.getParameterTypes();
// 如果参数个数与结果集个数不一致,则忽略该构造方法
if (parameterTypes.length != jdbcTypes.size()) {
return false;
}
// 遍历所有的构造参数,根据构造参数的顺序与结果集中的列的顺序进行匹配
for (int i = 0; i < parameterTypes.length; i++) {
// 只要有一组类型匹配不上,则忽略该构造方法
if (!typeHandlerRegistry.hasTypeHandler(parameterTypes[i], jdbcTypes.get(i))) {
return false;
}
}
return true;
}
// 执行构造参数的自动映射
public Object applyConstructorAutomapping(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, Constructor<?> constructor) throws SQLException {
boolean foundValues = false;
// 是否开启了使用构造参数名称进行映射
if (configuration.argNameBasedConstructorAutoMapping) {
// 使用构造参数名称进行映射
foundValues = applyArgNameBasedConstructorAutoMapping(rsw, resultMap, columnPrefix, constructorArgTypes, constructorArgs, constructor, foundValues);
}
// 使用列顺序进行映射
else {
// 使用结果集列的顺序进行一一映射
foundValues = applyColumnOrderBasedConstructorAutomapping(rsw, constructorArgTypes, constructorArgs, constructor, foundValues);
}
// 如果找到值或者设置了结果集为null值需要使用默认对象代替的话,使用结果对象已经构造参数信息创建对象,否则直接返回hull
return foundValues || configuration.returnInstanceForEmptyRow ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
}
// 使用结果集列的顺序进行一一映射
public boolean applyColumnOrderBasedConstructorAutomapping(ResultSetWrapper rsw, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, Constructor<?> constructor, boolean foundValues) throws SQLException {
// 遍历所有的构造参数
for (int i = 0; i < constructor.getParameterTypes().length; i++) {
// 获取参数类型
Class<?> parameterType = constructor.getParameterTypes()[i];
// 获取列名
String columnName = rsw.getColumnNames().get(i);
// 获取参数java类型以及列名对应的类型转换器
TypeHandler<?> typeHandler = rsw.getTypeHandler(parameterType, columnName);
// 使用转换器进行转换
Object value = typeHandler.getResult(rsw.getResultSet(), columnName);
// 保存该构造参数类型
constructorArgTypes.add(parameterType);
// 保存该构造参数值
constructorArgs.add(value);
// 标记找到了构造参数值
foundValues = value != null || foundValues;
}
return foundValues;
}
// 通过构造参数名称进行映射
public boolean applyArgNameBasedConstructorAutoMapping(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, Constructor<?> constructor, boolean foundValues) throws SQLException {
List<String> missingArgs = null;
// 获取构造函数的所有参数
Parameter[] params = constructor.getParameters();
// 遍历所有参数
for (Parameter param : params) {
boolean columnNotFound = true;
// 是否存在参数注解
Param paramAnno = param.getAnnotation(Param.class);
// 获取到要使用的参数名
String paramName = paramAnno == null ? param.getName() : paramAnno.value();
// 遍历所有的列名
for (String columnName : rsw.getColumnNames()) {
// 使用列名与参数名进行匹配,如果给定了前缀,拼接前缀进行匹配,同时会处理下划线转驼峰
if (this.columnMatchesParam(columnName, paramName, columnPrefix)) {
// 获取参数类型
Class<?> paramType = param.getType();
// 获取参数对应的类型转换器
TypeHandler<?> typeHandler = rsw.getTypeHandler(paramType, columnName);
// 获取转换后的结果
Object value = typeHandler.getResult(rsw.getResultSet(), columnName);
// 保存到构造参数类型
constructorArgTypes.add(paramType);
// 保存构造参数的值
constructorArgs.add(value);
// 使用resultMap+列前缀作为
String mapKey = resultMap.getId() + ":" + columnPrefix;
// 如果缓存中不存在该Key的数据
if (!autoMappingsCache.containsKey(mapKey)) {
// 保存所有通过构造函数自动映射的列名信息进行缓存
MapUtil.computeIfAbsent(constructorAutoMappingColumns, mapKey, k -> new ArrayList<>()).add(columnName);
}
// 标记找到了映射的列名
columnNotFound = false;
// 标记是否找到了值对象
foundValues = value != null || foundValues;
}
}
// 如果没有找到映射的列名,将该列名先保存起来,后面需要校验
if (columnNotFound) {
if (missingArgs == null) {
missingArgs = new ArrayList<>();
}
missingArgs.add(paramName);
}
}
// 如果通过名称找了构造参数的值,但是根据构造参数名从列名中获取到的属性个数与实际参数不一致时,需要抛出异常
// 说白了就是我构造参数有3个,但是根据构造参数名在列数据中只找到了两个,这个时候抛出异常,如果一个都没有找到,则不会抛出异常
if (foundValues && constructorArgs.size() < params.length) {
throw new ExecutorException(MessageFormat.format(
"Constructor auto-mapping of ''{1}'' failed " + "because ''{0}'' were not found in the result set; "
+ "Available columns are ''{2}'' and mapUnderscoreToCamelCase is ''{3}''.",
missingArgs, constructor, rsw.getColumnNames(), configuration.isMapUnderscoreToCamelCase()));
}
return foundValues;
}
// 使用列名与参数名进行匹配,如果给定了前缀,拼接前缀进行匹配,同时会处理下划线转驼峰
public boolean columnMatchesParam(String columnName, String paramName, String columnPrefix) {
// 如果存在前缀
if (columnPrefix != null) {
// 列名需要拼接前缀进行匹配,如果列名不存在前缀,就没必要匹配了,肯定匹配不上
if (!columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
return false;
}
// 使用列名拼接前缀进行匹配
columnName = columnName.substring(columnPrefix.length());
}
// 如果配置了开启驼峰映射,则使用技巧,将列名的所有"_"删除之后,全部转换为小写来对比
return paramName.equalsIgnoreCase(configuration.isMapUnderscoreToCamelCase() ? columnName.replace("_", "") : columnName);
}
// 执行自动映射操作,将未能映射上的列或者没有设置映射关系的列尝试进行自动映射
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
// 创建未映射能映射成功的列名的自动映射信息(也就是在resultMap中的column列名与结果集列名不匹配的所有列名)
// 返回这些未能映射但是可以通过属性名自动映射的列名
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
// 如果存在需要自动映射的列名
if (!autoMapping.isEmpty()) {
// 遍历所有自动映射字段
for (UnMappedColumnAutoMapping mapping : autoMapping) {
// 通过类型转换器转换结果
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
// 如果转换器提供了结果对象
if (value != null) {
// 标记找到了映射的值
foundValues = true;
}
// 如果映射的值不为null,或者配置了无论值是否为空,都要设置(忽略简单类型)
if (value != null || configuration.callSettersOnNulls && !mapping.primitive) {
// 将属性设置到结果对象中
metaObject.setValue(mapping.property, value);
}
}
}
return foundValues;
}
// 创建未映射能映射成功的列名的自动映射信息(也就是在resultMap中的column列名与结果集列名不匹配的所有列名)
// 返回这些未能映射但是可以通过属性名自动映射的列名
public List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
// 缓存自动映射列信息的key
String mapKey = resultMap.getId() + ":" + columnPrefix;
// 之前是否缓存过该resultMap的自动映射的列信息
List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
// 如果没有缓存,就要开始创建自动映射的列信息
if (autoMapping == null) {
autoMapping = new ArrayList<>();
// 获取所有在resultMap中的column列名与结果集列名不匹配的所有列名(前提是填写了column属性)
List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
// 如果是当前结果对象是基于构造函数的自动映射创建的对象的话,会将自动映射的列名缓存到constructorAutoMappingColumns中
List<String> mappedInConstructorAutoMapping = constructorAutoMappingColumns.remove(mapKey);
// 先要排除构造映射的字段,不需要再映射构造字段
if (mappedInConstructorAutoMapping != null) {
unmappedColumnNames.removeAll(mappedInConstructorAutoMapping);
}
// 遍历所有未能映射成功的列名
for (String columnName : unmappedColumnNames) {
// 获取属性名,自动映射的情况下列名就是属性名
String propertyName = columnName;
if (columnPrefix != null && !columnPrefix.isEmpty()) {
// 当指定columnPrefix时,忽略没有前缀的列
if (!columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
continue;
}
// 拼接前缀
propertyName = columnName.substring(columnPrefix.length());
}
// 从结果对象中找该属性名对应的属性
String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
// 如果找到了,并且存在Set方法
if (property != null && metaObject.hasSetter(property)) {
// 在resultMap中字段映射设置的property属性名,如果设置property属性,保存到映射属性名的集合中
// 也就是设置了手动映射的字段要忽略
if (resultMap.mappedProperties.contains(property)) {
continue;
}
// 获取属性类型
Class<?> propertyType = metaObject.getSetterType(property);
// 是否存在该属性类型的类型转换器
if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
// 获取类型转换器
TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
// 保存需要进行自动映射的列信息
autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
}
// 如果自动映射的情况下,映射的属性并没有对应的类型转换器,也就无法进行转换
else {
// 执行无法映射列的行为操作
configuration.autoMappingUnknownColumnBehavior.doAction(mappedStatement, columnName, property, propertyType);
}
}
// 如果存在列名,但是并没有找到给列对应的属性
else {
// 执行无法映射列的行为操作
configuration.autoMappingUnknownColumnBehavior().doAction(mappedStatement, columnName, property != null ? property : propertyName, null);
}
}
// 缓存该resultMap中,可以自动映射成功的列名信息
autoMappingsCache.put(mapKey, autoMapping);
}
return autoMapping;
}
// 是否需要进行自动映射
public boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) {
// 在resultMap中明确指定了自动映射的值
if (resultMap.getAutoMapping() != null) {
return resultMap.getAutoMapping();
}
// 如果是嵌套映射
// 指定MyBatis应如何自动映射列到字段或属性存在三个
// NONE 表示关闭自动映射;
// PARTIAL 只会自动映射没有定义嵌套结果映射的字段(默认)
// FULL 会自动映射任何复杂的结果集(无论是否嵌套)
if (isNested) {
return AutoMappingBehavior.FULL == configuration.autoMappingBehavior();
}
// 如果不是嵌套的话,只要没有关闭自动映射,那么就会使用自动映射
else {
return AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior();
}
}
/**
* 嵌套ResultMap的父对象缓存,主要用于复杂的结果集映射,缓存上一层的父对象
* 例如,正在映射allResultMap -> User , 由于现在是处理嵌套映射,那么当映射Role的时候使用的父对象必然是在User对象
* 缓存父对象主要是为了解决循环依赖,例如在User中包含Role,在Role中又包含User
* <pre>
* 案例
* <resultMap id="allResultMap" type="Users">
* <association property="roles" javaType="Roles" resultMap="roleResultMap">
* <id property="id" column="id"/>
* 此时,Roles对象中,又引用了allResultMap,也就是User对象这个ResultMap
* <association property="users" javaType="Users" resultMap="allResultMap"/>
* </association>
* </resultMap>
* </pre>
* 因此在执行User的嵌套属性映射Role时候,需要将User对象缓存,到时候Role映射完之后,设置到该User对象中,如果不缓存,每次创建对象
* <pre>
* 例如: class User{
* Role role;
* }
* class Role{
* User user;
* }
* </pre>
*
* @param resultObject 缓存的结果对象
* @param resultMapId 该结果对象对应的resultMap,也就是在该resultMap的返回类型
*/
public void putAncestor(Object resultObject, String resultMapId) {
ancestorObjects.put(resultMapId, resultObject);
}
// 是否在任何一个非空列中存在值,也就是只要列存在数据,就可以
public boolean anyNotNullColumnHasValue(ResultMapping resultMapping, String columnPrefix, ResultSetWrapper rsw) throws SQLException {
// 获取所有的非空列的列名
Set<String> notNullColumns = resultMapping.notNullColumns;
// 如果存在非空列
if (notNullColumns != null && !notNullColumns.isEmpty()) {
// 获取结果集
ResultSet rs = rsw.getResultSet();
// 遍历列名
for (String column : notNullColumns) {
// 拼接前缀
String columnName = prependPrefix(column, columnPrefix);
// 获取该列的值
rs.getObject(columnName);
// 如果结果集不为空,表示该列不为空
if (!rs.wasNull()) {
return true;
}
}
// 没有列存在数据
return false;
}
// 如果没用设置非空列,但是设置了前缀,使用前缀比较
if (columnPrefix != null) {
// 遍历结果集中所有的列
for (String columnName : rsw.getColumnNames()) {
// 如果结果集中包含了以指定前缀开头的字段,则返回true
if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix.toUpperCase(Locale.ENGLISH))) {
return true;
}
}
return false;
}
// 如果前缀也没有设置,直接返回true
return true;
}
// 将指定属性的值设置到结果对象中
public void linkObjects(MetaObject metaObject, ResultMapping resultMapping, Object rowValue) {
// 初始化集合属性,如果是集合类型的字段,并且结果也是集合类型,那么直接赋值,如果不是集合类型,则创建空集合对象,其他类型返回null
Object collectionProperty = instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);
// 如果该字段是集合类型,那么将当前列的值保存到集合对象中
if (collectionProperty != null) {
MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty);
targetMetaObject.add(rowValue);
}
// 如果当前列的值不是集合,则直接将值设置到对象中
else {
metaObject.setValue(resultMapping.getProperty(), rowValue);
}
}
// 根据resultMapId从配置类中ResultMap信息
public ResultMap getNestedResultMap(ResultSet rs, String nestedResultMapId, String columnPrefix) throws SQLException {
// 从配置类中获取该ID对应的resultMap
ResultMap nestedResultMap = configuration.getResultMap(nestedResultMapId);
// 处理Discriminator,按照分支来决定使用的ResultMap
return resolveDiscriminatedResultMap(rs, nestedResultMap, columnPrefix);
}
// 获取列的前缀
private String getColumnPrefix(String parentPrefix, ResultMapping resultMapping) {
final StringBuilder columnPrefixBuilder = new StringBuilder();
// 如果存在上一级的前缀
if (parentPrefix != null) {
// 拼接
columnPrefixBuilder.append(parentPrefix);
}
// 如果当前映射字段存在前缀,继续拼接前缀
if (resultMapping.getColumnPrefix() != null) {
columnPrefixBuilder.append(resultMapping.getColumnPrefix());
}
return columnPrefixBuilder.length() == 0 ? null : columnPrefixBuilder.toString().toUpperCase(Locale.ENGLISH);
}
// 初始化集合属性,如果是集合类型的字段,并且结果也是集合类型,那么直接赋值,如果不是集合类型,则创建空集合对象,其他类型返回null
public Object instantiateCollectionPropertyIfAppropriate(ResultMapping resultMapping, MetaObject metaObject) {
// 获取属性名
String propertyName = resultMapping.getProperty();
// 获取属性值
Object propertyValue = metaObject.getValue(propertyName);
// 如果不存在属性值
if (propertyValue == null) {
// 获取映射关系的java类型
Class<?> type = resultMapping.getJavaType();
// 如果为设置java类型
if (type == null) {
// 使用结果对象中属性名对应的类型
type = metaObject.getSetterType(propertyName);
}
// 如果属性对应的类型为集合
if (objectFactory.isCollection(type)) {
// 创建集合对象
propertyValue = objectFactory.create(type);
// 将创建的结合设置到结果对象中
metaObject.setValue(propertyName, propertyValue);
// 返回创建的结果对象
return propertyValue;
}
}
// 如果存在属性值,并且属性值为集合类型,直接返回该值
else if (objectFactory.isCollection(propertyValue.getClass())) {
return propertyValue;
}
// 如果存在值,但是属性类型不是集合,无法赋值,返回hull
return null;
}
/**
* 因为是嵌套关系,因此将嵌套处理过程的中数据进行缓存,这里创建缓存的Key
* 在不同情况下,使用不同的数据作为缓存Key,这个缓存key是有讲究的
* <pre>
* 例如: 在这个嵌套结果集的情况下:
* uid username role_name permissions_name
* 1 luck admin select
* 1 luck admin delete
* 1 luck admin update
* 1 luck admin insert
*
* 映射为下面的结果,我们只想映射为一个User对象
* class User{
* Long uid;
* String username;
* Role role;
* List<Permission> permissions;
* }
* 先创建User对象, 映射uid和username字段
* 接着可以通过uid=1 username=luck 作为一级缓存的key
* 在映射role属性的时候 映射role_name
* 接着可以通过 uid=1 username=luck role_name=admin 作为二级缓存的key(也就是一级缓存的key+二级缓存的字段)
* 接着映射permissions属性的时候,映射permissions_name
* 接着可以通过 uid=1 username=luck permissions_name = [select,delete,update,insert] 作为另一个二级缓存的key(也就是一级缓存的key+二级缓存的字段)
* 这个时候,当我们处理好了这些高阶的缓存(role或者permission)之后,此时可以为user设置属性
* 我们根据每一个行的uid=1 username=luck role_name=admin来从缓存中获取数据都能获取到第一次缓存进去的数据,这样role对象只需要创建一次
* 我们根据每一个行的
* uid=1 username=luck permissions_name=select
* uid=1 username=luck permissions_name=delete
* uid=1 username=luck permissions_name=update
* uid=1 username=luck permissions_name=insert
* 来从缓存中获取数据,都是获取不到的,因此对于每一个permission对象,都需要创建新的,因此会有四个permission对象,一个role对象
* 而我们根据每一个行的uid=1 username=luck来从缓存中获取数据也都能获取到第一次缓存进去的user数据,这样user对象只需要创建一次
* 一次最终创建的结果就是user对象一个,role对象一个,permission对象4个,组成了User类的这种关系
*
* 当然,如何设置了id映射,那么就不需要缓存这么多字段的映射信息,只需要将id属性缓存即可,因为id属性是不会重复的,相同的id为相同的一组是正常的
*
*
* 1. 在resultMap中没有填写任何字段映射的情况下,有两种情况
* 1.1. 如果resultMap的返回类型是Map,那么缓存Key的数据为结果集中的所有键值对(列名和列值)
* 1.2. 如果resultMap返回的不是Map,而是一个普通对象,那么缓存key的数据为 在使用的resultMap中,未使用column指定要映射的列名的列和列值
* 2. 在resultMap指定了映射字段的情况下
* 2.1. 那么缓存key的数据为: 在使用的resultMap中,使用column指定了要映射的列名的列和列值作为缓存Key的数据
* </pre>
*
* @param resultMap 正在处理的resultMap映射
* @param rsw 结果集包装对象
* @param columnPrefix 列名前缀
* @return 缓存的Key信息
*/
public CacheKey createRowKey(ResultMap resultMap, ResultSetWrapper rsw, String columnPrefix) throws SQLException {
CacheKey cacheKey = new CacheKey();
// resultMapId
cacheKey.update(resultMap.getId());
// 获取该结果集映射中的映射关系,先获取ID类型的列映射,果不存在ID类型的列映射,则获取所有的普通属性映射
List<ResultMapping> resultMappings = getResultMappingsForRowKey(resultMap);
// 如果该result没有填写任何列映射信息
if (resultMappings.isEmpty()) {
// 如果resultMap需要返回的是Map类型,最简单,就是将列名映射到列名的值
if (Map.class.isAssignableFrom(resultMap.getType())) {
// 创建这一行结果的缓存Key
// 将结果集中所有列名和列值都作为缓存key中的一部分
this.createRowKeyForMap(rsw, cacheKey);
} else {
// 如果不是返回的不是Map,是某个对象
// 创建这一行结果的缓存Key
// 使用所有在resultMap中,未使用column指定要映射的列名的列和列值作为缓存Key的数据
this.createRowKeyForUnmappedProperties(resultMap, rsw, cacheKey, columnPrefix);
}
}
// 如果存在resultMap的映射字段信息
else {
// 创建这一行结果的缓存Key
// 使用所有在resultMap中,使用column指定了要映射的列名的列和列值作为缓存Key的数据
this.createRowKeyForMappedProperties(resultMap, rsw, cacheKey, resultMappings, columnPrefix);
}
// 因为设置缓存key的时候是一组一组的设置,所以如果小于2,表示数据缺失,则返回一个固定的空缓存KEY
if (cacheKey.getUpdateCount() < 2) {
return CacheKey.NULL_CACHE_KEY;
}
// 正常情况返回创建并处理好的缓存key
return cacheKey;
}
// 获取该结果集映射中的映射关系,先获取ID类型的列映射,果不存在ID类型的列映射,则获取所有的普通属性映射
public List<ResultMapping> getResultMappingsForRowKey(ResultMap resultMap) {
// 先获取ID类型的列映射
List<ResultMapping> resultMappings = resultMap.getIdResultMappings();
// 如果不存在ID类型的列映射,则获取所有的普通属性映射
if (resultMappings.isEmpty()) {
resultMappings = resultMap.getPropertyResultMappings();
}
return resultMappings;
}
// 使用所有在resultMap中,使用column指定了要映射的列名的列和列值作为缓存Key的数据
public void createRowKeyForMappedProperties(ResultMap resultMap, ResultSetWrapper rsw, CacheKey cacheKey, List<ResultMapping> resultMappings, String columnPrefix) throws SQLException {
// 遍历所有映射关系
for (ResultMapping resultMapping : resultMappings) {
// 如果是简单的resultMap映射,不存在嵌套resultMap或者嵌套查询
if (resultMapping.isSimple()) {
// 拼接列前缀
String column = prependPrefix(resultMapping.getColumn(), columnPrefix);
// 获取指定的类型转换器
TypeHandler<?> th = resultMapping.getTypeHandler();
// 获取所有在resultMap中的column列名与结果集列名匹配的所有列名(前提是填写了column属性)
List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
// 如果拼接上前缀之后,在映射的列名中存在,表示可以映射
if (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) {
// 使用类型转换器对该列的值进行类型转换
Object value = th.getResult(rsw.getResultSet(), column);
// 如果获取到列值,或者配置类设置了即使没有值也会返回空实例对象
// 将列名和列值当做缓存key
if (value != null || configuration.isReturnInstanceForEmptyRow()) {
cacheKey.update(column);
cacheKey.update(value);
}
}
}
}
}
// 创建这一行结果的缓存Key
// 所有在resultMap中的column列名与结果集列名不匹配的所有列名(前提是填写了column属性)列名的列和列值作为缓存Key的数据
public void createRowKeyForUnmappedProperties(ResultMap resultMap, ResultSetWrapper rsw, CacheKey cacheKey, String columnPrefix) throws SQLException {
// 根据resultMap的返回类型封装成可动态反射操作的class元数据
MetaClass metaType = MetaClass.forClass(resultMap.getType(), reflectorFactory);
// 获取所有在resultMap中的column列名与结果集列名不匹配的所有列名(前提是填写了column属性)
List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
// 遍历所有未映射上的列名
for (String column : unmappedColumnNames) {
String property = column;
if (columnPrefix != null && !columnPrefix.isEmpty()) {
// 当指定columnPrefix列前缀时,忽略没有指定前缀的列
if (!column.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
continue;
}
// 使用列名拼接前缀
property = column.substring(columnPrefix.length());
}
// 在结果对象中找对应的属性(根据配置信息,会将列名的下换线转驼峰)
// 由于resultMap中的列和结果集中的列没有匹配上,那么,尝试使用列名和结果对象的属性匹配一下
// 如果属性匹配成功,那么就会绕过resultMap,直接使用属性映射
// 如果在结果对象中也没有找到该列对应的属性,则会忽略该字段
if (metaType.findProperty(property, configuration.isMapUnderscoreToCamelCase()) != null) {
// 如果找到该列名映对应的属性,获取该列的值
String value = rsw.getResultSet().getString(column);
// 将列名和列值当做缓存key
if (value != null) {
cacheKey.update(column);
cacheKey.update(value);
}
}
}
}
// 创建这一行结果的缓存Key
// 将所有列名和列值都作为缓存key中的一部分
public void createRowKeyForMap(ResultSetWrapper rsw, CacheKey cacheKey) throws SQLException {
// 获取所有列名
List<String> columnNames = rsw.getColumnNames();
// 遍历所有列名
for (String columnName : columnNames) {
// 获取列值
String value = rsw.getResultSet().getString(columnName);
// 如果存在列值
if (value != null) {
// 缓存列名和列名
cacheKey.update(columnName);
cacheKey.update(value);
}
}
}
// 将指定属性的值设置到结果对象中
public void linkObjects(MetaObject metaObject, ResultMapping resultMapping, Object rowValue) {
// 初始化集合属性,如果是集合类型的字段,并且结果也是集合类型,那么直接赋值,如果不是集合类型,则创建空集合对象,其他类型返回null
Object collectionProperty = instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);
// 如果该字段是集合类型,那么将当前列的值保存到集合对象中
if (collectionProperty != null) {
MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty);
targetMetaObject.add(rowValue);
}
// 如果当前列的值不是集合,则直接将值设置到对象中
else {
metaObject.setValue(resultMapping.getProperty(), rowValue);
}
}
/**
* 根据discriminated标签,决定具体应该使用哪一个resultMap,如果不存在discriminator,则直接返回给定的resultMap,如果存在,返回经过映射列合并的resultMap
* 因为在resultMap中discriminator必须在最后的位置书写,也就意味着处理discriminator的时候,其他映射字段已经解析成resultMapping了
* mybatis的处理逻辑是,将discriminator中case的映射关系,加上之前所有解析的标签对应的的映射关系(resultMapping)进行合并成一个新的resultMap
* 最终使用某个一resultMap的时候,会判断resultMap是否存在discriminator,如果存在discriminator则会使用合并映射列之后的新resultMap来替换当前使用的resultMap
* 因为替换之后的那个新的resultMap包含了全部的映射列信息,而原来那个resultMap不包含discriminator中的映射列信息
*/
public ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) throws SQLException {
Set<String> pastDiscriminators = new HashSet<>();
// 获取Discriminator信息
Discriminator discriminator = resultMap.discriminator;
// 如果resultMap中存在分支鉴别器,递归处理,如果没有直接返回原始的resultMap
// 递归处理Discriminator,因为discriminator根据值会指定到具体的resultMap,而resultMap中可能又会有discriminator
while (discriminator != null) {
// 获取Discriminator分支的值
Object value = getDiscriminatorValue(rs, discriminator, columnPrefix);
// 根据值来获取到指定的resultMap,保存的时候key: 值 - value: resultMapId
String discriminatedMapId = discriminator.getMapIdFor(String.valueOf(value));
// 如果配置对象中不包含这个resultMap,不处理
if (!configuration.hasResultMap(discriminatedMapId)) {
break;
}
// 获取discriminator对应值的这个resultMap,因为discriminator对应值的这个resultMap,对discriminator所在的那个resultMap进行列映射合并成了新的resultMap
// 所以,如果存在discriminator,就要使用合并后的resultMap,因为合并后的resultMap才包含原resultMap+case中的映射列关系
resultMap = configuration.getResultMap(discriminatedMapId);
// 保存当前处理的这个discriminator
Discriminator lastDiscriminator = discriminator;
// 当前处理的discriminator值对应的resultMap中,是否也有discriminator,如果有,也要处理
discriminator = resultMap.getDiscriminator();
// discriminator值对应的resultMap就是当前本身的resultMap,就不需要处理,直接返回当前resultMap
// 或者discriminator的值对应的resultMap已经被处理过了,因为处理之后就会保存到pastDiscriminators中,重复处理就会添加失败
if (discriminator == lastDiscriminator || !pastDiscriminators.add(discriminatedMapId)) {
break;
}
}
// 如果存在嵌套的Discriminator,则返回Discriminator分支的resultMap来做映射,否则原样返回传递来的resultMap
// 因为discriminator对应值的这个resultMap,对discriminator所在的那个resultMap进行列映射合并成了新的resultMap
// 所以,如果存在discriminator,就要使用合并后的resultMap,因为合并后的resultMap才包含原resultMap+case中的映射列关系
return resultMap;
}
// 获取Discriminator分支的值
public Object getDiscriminatorValue(ResultSet rs, Discriminator discriminator, String columnPrefix) throws SQLException {
// 获取结果的映射信息
ResultMapping resultMapping = discriminator.getResultMapping();
// 获取类型处理器
TypeHandler<?> typeHandler = resultMapping.getTypeHandler();
// 使用类型转换器从结果集中,获取指定列(带有前缀)的值
return typeHandler.getResult(rs, prependPrefix(resultMapping.getColumn(), columnPrefix));
}
// 拼接列前缀,将指定列名拼接列前缀返回
public String prependPrefix(String columnName, String prefix) {
if (columnName == null || columnName.length() == 0 || prefix == null || prefix.length() == 0) {
return columnName;
}
return prefix + columnName;
}
// 是否需要继续处理
public boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) {
// 上下文对象没有设置停止处理结果集,并且处理的结果数量没有达到分页限制
return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();
}
// 分页跳到指定的行
public void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
// 如果结果集类型不是TYPE_FORWARD_ONLY,则直接使用absolute定位到某一行
if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
rs.absolute(rowBounds.getOffset());
}
return;
}
// 如果结果集类型是TYPE_FORWARD_ONLY,那就要忽略结果,直到指定的位置
for (int i = 0; i < rowBounds.getOffset(); i++) {
if (!rs.next()) {
break;
}
}
}
// 注意: 校验带有嵌套结果映射的映射语句不能与自定义ResultHandler一起使用
// 使用safeResultHandlerEnabled=false设置绕过此检查
// 或设置resultOrdered=true,映射的时候会按照sql的查询列的顺序进行一一映射
protected void checkResultHandler() {
if (resultHandler != null && configuration.isSafeResultHandlerEnabled() && !mappedStatement.resultOrdered()) {
throw new ExecutorException("Mapped Statements with nested result mappings cannot be safely used with a custom ResultHandler. " + "Use safeResultHandlerEnabled=false setting to bypass this check " + "or ensure your statement returns ordered data and set resultOrdered=true on it.");
}
}
// 校验分页的边界问题
public void ensureNoRowBounds() {
if (configuration.isSafeRowBoundsEnabled() && rowBounds != null && (rowBounds.getLimit() < RowBounds.NO_ROW_LIMIT || rowBounds.getOffset() > RowBounds.NO_ROW_OFFSET)) {
throw new ExecutorException("Mapped Statements with nested result mappings cannot be safely constrained by RowBounds. " + "Use safeRowBoundsEnabled=false setting to bypass this check.");
}
}
// 校验结果集个数,必须存在resultMap
public void validateResultMapsCount(ResultSetWrapper rsw, int resultMapCount) {
if (rsw != null && resultMapCount < 1) {
throw new ExecutorException("A query was run and no Result Maps were found for the Mapped Statement '" + mappedStatement.getId() + "'. 'resultType' or 'resultMap' must be specified when there is no corresponding method.");
}
}
// 获取查询返回结果集中的第一个结果集对象,因为可能存在多个结果集的情况
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
// 获取结果集对象
ResultSet rs = stmt.getResultSet();
// 只有在多结果集的情况下才为null
while (rs == null) {
// 是否存在多结果集
if (stmt.getMoreResults()) {
// 获取结果集
rs = stmt.getResultSet();
} else if (stmt.getUpdateCount() == -1) {
break;
}
}
// 返回获取的结果集对象
return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}
// 因为getFirstResultSet获取了第一个结果集进行处理,接下来要处理获下一个结果集
private ResultSetWrapper getNextResultSet(Statement stmt) {
try {
// JDBC是否支持多结果集并且存在多结果集
if (stmt.getConnection().getMetaData().supportsMultipleResultSets() && (stmt.getMoreResults() || (stmt.getUpdateCount() != -1))) {
// 获取结果集对象
ResultSet rs = stmt.getResultSet();
// 递归获取结果集
if (rs == null) {
return getNextResultSet(stmt);
}
// 返回包装好的结果集对象
return new ResultSetWrapper(rs, configuration);
}
} catch (Exception e) {
// 出现以,忽略
}
// 如果所有结果集都处理完比,返回null
return null;
}
}
// 延迟加载的映射信息
public static class PendingRelation {
// 结果对象
public MetaObject metaObject;
// 该列的映射信息
public ResultMapping propertyMapping;
}
public static class UnMappedColumnAutoMapping {
// 列名
public String column;
// 属性名
public String property;
// 类型转换器
public TypeHandler<?> typeHandler;
// 是否为简单类型
public boolean primitive;
}
}