mybatis-ResultSetHandler

Posted siye1989

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis-ResultSetHandler相关的知识,希望对你有一定的参考价值。

1. 概述

本文,我们来分享 SQL 执行的第四部分,SQL 执行后,响应的结果集 ResultSet 的处理,涉及 executor/resultsetexecutor/resultcursor 包。整体类图如下:技术图片

  • 核心类是 ResultSetHandler 接口及其实现类 DefaultResultSetHandler 。在它的代码逻辑中,会调用类图中的其它类,实现将查询结果的 ResultSet ,转换成映射的对应结果。

2. ResultSetWrapper

老艿艿:在看具体的 DefaultResultSetHandler 的实现代码之前,我们先看看 ResultSetWrapper 的代码。因为 DefaultResultSetHandler 对 ResultSetWrapper 的调用比较多,避免混着解析。

org.apache.ibatis.executor.resultset.ResultSetWrapper ,java.sql.ResultSet 的 包装器,可以理解成 ResultSet 的工具类,提供给 DefaultResultSetHandler 使用。

2.1 构造方法

// ResultSetWrapper.java

/**
* ResultSet 对象
*/
private final ResultSet resultSet;
private final TypeHandlerRegistry typeHandlerRegistry;
/**
* 字段的名字的数组
*/
private final List<String> columnNames = new ArrayList<>();
/**
* 字段的 Java Type 的数组
*/
private final List<String> classNames = new ArrayList<>();
/**
* 字段的 JdbcType 的数组
*/
private final List<JdbcType> jdbcTypes = new ArrayList<>();
private final Map<String, Map<Class<?>, TypeHandler<?>>> typeHandlerMap = new HashMap<>();
private final Map<String, List<String>> mappedColumnNamesMap = new HashMap<>();
private final Map<String, List<String>> unMappedColumnNamesMap = new HashMap<>();

public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.resultSet = rs;
// <1> 遍历 ResultSetMetaData 的字段们,解析出 columnNames、jdbcTypes、classNames 属性
final ResultSetMetaData metaData = rs.getMetaData();
final int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++)
columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));
jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
classNames.add(metaData.getColumnClassName(i));


  • resultSet 属性,被包装的 ResultSet 对象。
  • columnNamesclassNamesjdbcTypes 属性,在 <1> 处,通过遍历 ResultSetMetaData 的字段们,从而解析出来。

2.2 getTypeHandler

// ResultSetWrapper.java

/**
* TypeHandler 的映射
*
* KEY1:字段的名字
* KEY2:Java 属性类型
*/
private final Map<String, Map<Class<?>, TypeHandler<?>>> typeHandlerMap = new HashMap<>();

/**
* Gets the type handler to use when reading the result set.
* Tries to get from the TypeHandlerRegistry by searching for the property type.
* If not found it gets the column JDBC type and tries to get a handler for it.
*
* 获得指定字段名的指定 JavaType 类型的 TypeHandler 对象
*
* @param propertyType JavaType
* @param columnName 执行字段
* @return TypeHandler 对象
*/
public TypeHandler<?> getTypeHandler(Class<?> propertyType, String columnName)
TypeHandler<?> handler = null;
// <1> 先从缓存的 typeHandlerMap 中,获得指定字段名的指定 JavaType 类型的 TypeHandler 对象
Map<Class<?>, TypeHandler<?>> columnHandlers = typeHandlerMap.get(columnName);
if (columnHandlers == null)
columnHandlers = new HashMap<>();
typeHandlerMap.put(columnName, columnHandlers);
else
handler = columnHandlers.get(propertyType);

// <2> 如果获取不到,则进行查找
if (handler == null)
// <2> 获得 JdbcType 类型
JdbcType jdbcType = getJdbcType(columnName);
// <2> 获得 TypeHandler 对象
handler = typeHandlerRegistry.getTypeHandler(propertyType, jdbcType);
// Replicate logic of UnknownTypeHandler#resolveTypeHandler
// See issue #59 comment 10
// <3> 如果获取不到,则再次进行查找
if (handler == null || handler instanceof UnknownTypeHandler)
// <3> 使用 classNames 中的类型,进行继续查找 TypeHandler 对象
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);


// <4> 如果获取不到,则使用 ObjectTypeHandler 对象
if (handler == null || handler instanceof UnknownTypeHandler)
handler = new ObjectTypeHandler();

// <5> 缓存到 typeHandlerMap 中
columnHandlers.put(propertyType, handler);

return handler;

  • <1> 处,先从缓存的 typeHandlerMap 中,获得指定字段名的指定 JavaType 类型的 TypeHandler 对象。
  • <2> 处,如果获取不到,则基于 propertyType + jdbcType 进行查找。其中,#getJdbcType(String columnName) 方法,获得 JdbcType 类型。代码如下:

    // ResultSetWrapper.java

    public JdbcType getJdbcType(String columnName)
    for (int i = 0; i < columnNames.size(); i++)
    if (columnNames.get(i).equalsIgnoreCase(columnName))
    return jdbcTypes.get(i);


    return null;

    • 通过 columnNames 索引到位置 i ,从而到 jdbcTypes 中获得 JdbcType 类型。
  • <3> 处,如果获取不到,则基于 javaType + jdbcType 进行查找。其中,javaType 使用 classNames 中的类型。而 #resolveClass(String className) 方法,获得对应的类。代码如下:

    // ResultSetWrapper.java

    private Class<?> resolveClass(String className)
    try
    // #699 className could be null
    if (className != null)
    return Resources.classForName(className);

    catch (ClassNotFoundException e)
    // ignore

    return null;

  • <4> 处,如果获取不到,则使用 ObjectTypeHandler 对象。

  • <5> 处,缓存 TypeHandler 对象,到 typeHandlerMap 中。

2.3 loadMappedAndUnmappedColumnNames

#loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) 方法,初始化有 mapped 和无 mapped的字段的名字数组。代码如下:

// ResultSetWrapper.java

/**
* 有 mapped 的字段的名字的映射
*
* KEY:@link #getMapKey(ResultMap, String)
* VALUE:字段的名字的数组
*/
private final Map<String, List<String>> mappedColumnNamesMap = new HashMap<>();
/**
* 无 mapped 的字段的名字的映射
*
* 和 @link #mappedColumnNamesMap 相反
*/
private final Map<String, List<String>> unMappedColumnNamesMap = new HashMap<>();

private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException
List<String> mappedColumnNames = new ArrayList<>();
List<String> unmappedColumnNames = new ArrayList<>();
// <1> 将 columnPrefix 转换成大写,并拼接到 resultMap.mappedColumns 属性上
final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);
final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);
// <2> 遍历 columnNames 数组,根据是否在 mappedColumns 中,分别添加到 mappedColumnNames 和 unmappedColumnNames 中
for (String columnName : columnNames)
final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);
if (mappedColumns.contains(upperColumnName))
mappedColumnNames.add(upperColumnName);
else
unmappedColumnNames.add(columnName);


// <3> 将 mappedColumnNames 和 unmappedColumnNames 结果,添加到 mappedColumnNamesMap 和 unMappedColumnNamesMap 中
mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);
unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);

  • <1> 处,将 columnPrefix 转换成大写,后调用 #prependPrefixes(Set<String> columnNames, String prefix) 方法,拼接到 resultMap.mappedColumns 属性上。代码如下:

    // ResultSetWrapper.java

    private Set<String> prependPrefixes(Set<String> columnNames, String prefix)
    // 直接返回 columnNames ,如果符合如下任一情况
    if (columnNames == null || columnNames.isEmpty() || prefix == null || prefix.length() == 0)
    return columnNames;

    // 拼接前缀 prefix ,然后返回
    final Set<String> prefixed = new HashSet<>();
    for (String columnName : columnNames)
    prefixed.add(prefix + columnName);

    return prefixed;

    • 当然,可能有胖友,跟我会懵逼,可能已经忘记什么是 resultMap.mappedColumns 。我们来举个示例:

      <resultMap id="B" type="Object">
      <result property="year" column="year"/>
      </resultMap>

      <select id="testResultMap" parameterType="Integer" resultMap="A">
      SELECT * FROM subject
      </select>
      • 此处的 column="year" ,就会被添加到 resultMap.mappedColumns 属性上。
  • <2> 处,遍历 columnNames 数组,根据是否在 mappedColumns 中,分别添加到 mappedColumnNames 和 unmappedColumnNames 中。
  • <3> 处,将 mappedColumnNames 和 unmappedColumnNames 结果,添加到 mappedColumnNamesMap 和 unMappedColumnNamesMap 中。其中,#getMapKey(ResultMap resultMap, String columnPrefix) 方法,获得缓存的 KEY 。代码如下:

    // ResultSetWrapper.java

    private String getMapKey(ResultMap resultMap, String columnPrefix)
    return resultMap.getId() + ":" + columnPrefix;

下面,我们看个类似的,会调用该方法的方法:

  • #getMappedColumnNames(ResultMap resultMap, String columnPrefix) 方法,获得有 mapped 的字段的名字的数组。代码如下:

    // ResultSetWrapper.java

    public List<String> getMappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException
    // 获得对应的 mapped 数组
    List<String> mappedColumnNames = mappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
    if (mappedColumnNames == null)
    // 初始化
    loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
    // 重新获得对应的 mapped 数组
    mappedColumnNames = mappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));

    return mappedColumnNames;

  • #getUnmappedColumnNames(ResultMap resultMap, String columnPrefix) 方法,获得无 mapped 的字段的名字的数组。代码如下:

    // ResultSetWrapper.java

    public List<String> getUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException
    // 获得对应的 unMapped 数组
    List<String> unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
    if (unMappedColumnNames == null)
    // 初始化
    loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
    // 重新获得对应的 unMapped 数组
    unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));

    return unMappedColumnNames;

?? 具体这两个方法什么用途呢?待到我们在 DefaultResultSetHandler 类里来看。

3. ResultSetHandler

org.apache.ibatis.executor.resultset.ResultSetHandler ,java.sql.ResultSet 处理器接口。代码如下:

// ResultSetHandler.java

public interface ResultSetHandler

/**
* 处理 @link java.sql.ResultSet 成映射的对应的结果
*
* @param stmt Statement 对象
* @param <E> 泛型
* @return 结果数组
*/
<E> List<E> handleResultSets(Statement stmt) throws SQLException;

/**
* 处理 @link java.sql.ResultSet 成 Cursor 对象
*
* @param stmt Statement 对象
* @param <E> 泛型
* @return Cursor 对象
*/
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

// 暂时忽略,和存储过程相关
void handleOutputParameters(CallableStatement cs) throws SQLException;


3.1 DefaultResultSetHandler

老艿艿:保持冷静,DefaultResultSetHandler 有小 1000 行的代码。

org.apache.ibatis.executor.resultset.DefaultResultSetHandler ,实现 ResultSetHandler 接口,默认的 ResultSetHandler 实现类。

3.1.1 构造方法

// DefaultResultSetHandler.java

private static final Object DEFERED = new Object();

private final Executor executor;
private final Configuration configuration;
private final MappedStatement mappedStatement;
private final RowBounds rowBounds;
private final ParameterHandler parameterHandler;
/**
* 用户指定的用于处理结果的处理器。
*
* 一般情况下,不设置
*/
private final ResultHandler<?> resultHandler;
private final BoundSql boundSql;
private final TypeHandlerRegistry typeHandlerRegistry;
private final ObjectFactory objectFactory;
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
// 存储过程相关的多 ResultSet 涉及的属性,可以暂时忽略
private final Map<String, ResultMapping> nextResultMaps = new HashMap<>();
private final Map<CacheKey, List<PendingRelation>> pendingRelations = new HashMap<>();

// Cached Automappings
/**
* 自动映射的缓存
*
* KEY:@link ResultMap#getId() + ":" + columnPrefix
*
* @see #createRowKeyForUnmappedProperties(ResultMap, ResultSetWrapper, CacheKey, String)
*/
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;

3.1.2 handleResultSets

#handleResultSets(Statement stmt) 方法,处理 java.sql.ResultSet 结果集,转换成映射的对应结果。代码如下:

// DefaultResultSetHandler.java

@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

// <1> 多 ResultSet 的结果集合,每个 ResultSet 对应一个 Object 对象。而实际上,每个 Object 是 List<Object> 对象。
// 在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,multipleResults 最多就一个元素。
final List<Object> multipleResults = new ArrayList<>();

int resultSetCount = 0;
// <2> 获得首个 ResultSet 对象,并封装成 ResultSetWrapper 对象
ResultSetWrapper rsw = getFirstResultSet(stmt);

// <3> 获得 ResultMap 数组
// 在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,resultMaps 就一个元素。
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount); // <3.1> 校验
while (rsw != null && resultMapCount > resultSetCount)
// <4.1> 获得 ResultMap 对象
ResultMap resultMap = resultMaps.get(resultSetCount);
// <4.2> 处理 ResultSet ,将结果添加到 multipleResults 中
handleResultSet(rsw, resultMap, multipleResults, null);
// <4.3> 获得下一个 ResultSet 对象,并封装成 ResultSetWrapper 对象
rsw = getNextResultSet(stmt);
// <4.4> 清理
cleanUpAfterHandlingResultSet();
// resultSetCount ++
resultSetCount++;


// <5> 因为 `mappedStatement.resultSets` 只在存储过程中使用,本系列暂时不考虑,忽略即可
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null)
while (rsw != null && resultSetCount < resultSets.length)
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null)
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);

rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;



// <6> 如果是 multipleResults 单元素,则取首元素返回
return collapseSingleResultList(multipleResults);

  • 这个方法,不仅仅支持处理 Statement 和 PreparedStatement 返回的结果集,也支持存储过程的 CallableStatement 返回的结果集。而 CallableStatement 是支持返回多结果集的,这个大家要注意。?? 当然,还是老样子,本文不分析仅涉及存储过程的相关代码。哈哈哈。
  • <1> 处,多 ResultSet 的结果集合,每个 ResultSet 对应一个 Object 对象。而实际上,每个 Object 是 List 对象。在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,multipleResults 最多就一个元素。
  • <2> 处,调用 #getFirstResultSet(Statement stmt) 方法,获得首个 ResultSet 对象,并封装成 ResultSetWrapper 对象。代码如下:

    // DefaultResultSetHandler.java

    private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException
    ResultSet rs = stmt.getResultSet();
    // 可以忽略
    while (rs == null)
    // move forward to get the first resultset in case the driver
    // doesn‘t return the resultset as the first result (HSQLDB 2.1)
    if (stmt.getMoreResults())
    rs = stmt.getResultSet();
    else
    if (stmt.getUpdateCount() == -1)
    // no more results. Must be no resultset
    break;



    // 将 ResultSet 对象,封装成 ResultSetWrapper 对象
    return rs != null ? new ResultSetWrapper(rs, configuration) : null;

  • <3> 处,调用 MappedStatement#getResultMaps() 方法,获得 ResultMap 数组。在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,resultMaps 就一个元素。

    • <3.1> 处,调用 #validateResultMapsCount(ResultSetWrapper rsw, int resultMapCount) 方法,校验至少有一个 ResultMap 对象。代码如下:

      // DefaultResultSetHandler.java

      private 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()
      + "‘. It‘s likely that neither a Result Type nor a Result Map was specified.");


      • 不符合,则抛出 ExecutorException 异常。
  • <4.1> 处,获得 ResultMap 对象。
  • <4.2> 处,调用 #handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) 方法,处理 ResultSet ,将结果添加到 multipleResults 中。详细解析,见 「3.1.2.1 handleResultSet」 。
  • <4.3> 处,调用 #getNextResultSet(Statement stmt) 方法,获得下一个 ResultSet 对象,并封装成 ResultSetWrapper 对象。?? 只有存储过程才有多 ResultSet 对象,所以可以忽略。也就是说,实际上,这个 while 循环对我们来说,就不需要啦。
  • <4.4> 处,调用 #cleanUpAfterHandlingResultSet() 方法,执行清理。代码如下:

    // DefaultResultSetHandler.java

    private void cleanUpAfterHandlingResultSet()
    nestedResultObjects.clear();

  • <5> 处,因为 mappedStatement.resultSets 只在存储过程中使用,本系列暂时不考虑,忽略即可。

  • <6> 处,调用 #collapseSingleResultList(List<Object> multipleResults) 方法,如果是 multipleResults 单元素,则取首元素返回。代码如下:

    // DefaultResultSetHandler.java

    private List<Object> collapseSingleResultList(List<Object> multipleResults)
    return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;

    • 对于非存储过程的结果处理,都能符合 multipleResults.size() 。

3.1.2.1 handleResultSet

#handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) 方法,处理 ResultSet ,将结果添加到 multipleResults 中。代码如下:

// DefaultResultSetHandler.java

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException
try
// <1> 暂时忽略,因为只有存储过程的情况,调用该方法,parentMapping 为非空
if (parentMapping != null)
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
else
// <2> 如果没有自定义的 resultHandler ,则创建默认的 DefaultResultHandler 对象
if (resultHandler == null)
// <2> 创建 DefaultResultHandler 对象
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
// <3> 处理 ResultSet 返回的每一行 Row
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
// <4> 添加 defaultResultHandler 的处理的结果,到 multipleResults 中
multipleResults.add(defaultResultHandler.getResultList());
else
// <3> 处理 ResultSet 返回的每一行 Row
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);


finally
// issue #228 (close resultsets)
// 关闭 ResultSet 对象
closeResultSet(rsw.getResultSet());


  • <1> 处,暂时忽略,因为只有存储过程的情况,调用该方法,parentMapping 为非空。
  • <2> 处,如果没有自定义的 resultHandler ,则创建默认的 DefaultResultHandler 对象。
  • <3> 处,调用 #handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) 方法,处理 ResultSet 返回的每一行 Row 。详细解析,见 「3.1.2.2 handleRowValues」 。
  • 【特殊】<4> 处,使用默认的 DefaultResultHandler 对象,最终会将 defaultResultHandler 的处理的结果,到 multipleResults 中。而使用自定义的 resultHandler ,不会添加到 multipleResults 中。当然,因为自定义的 resultHandler 对象,是作为一个对象传入,所以在其内部,还是可以存储结果的。例如:

    @Select("select * from users")
    @ResultType(User.class)
    void getAllUsers(UserResultHandler resultHandler);
  • <5> 处,调用 #closeResultSet(ResultSet rs) 方法关闭 ResultSet 对象。代码如下:

    // DefaultResultSetHandler.java

    private void closeResultSet(ResultSet rs)
    try
    if (rs != null)
    rs.close();

    catch (SQLException e)
    // ignore


3.1.2.2 handleRowValues

#handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) 方法,处理 ResultSet 返回的每一行 Row 。代码如下:

// DefaultResultSetHandler.java

public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException
// <1> 处理嵌套映射的情况
if (resultMap.hasNestedResultMaps())
// 校验不要使用 RowBounds
ensureNoRowBounds();
// 校验不要使用自定义的 resultHandler
checkResultHandler();
// 处理嵌套映射的结果
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
// <2> 处理简单映射的情况
else
// <2.1> 处理简单映射的结果
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);


  • 分成嵌套映射和简单映射的两种情况。
  • <2> 处理嵌套映射的情况:
  • <1> 处理嵌套映射的情况:

    • <1.1> 处,调用 #ensureNoRowBounds() 方法,校验不要使用 RowBounds 。代码如下:

      // DefaultResultSetHandler.java

      private void ensureNoRowBounds()
      // configuration.isSafeRowBoundsEnabled() 默认为 false
      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.");


      • 简单看看即可。
    • <1.2> 处,调用 #checkResultHandler() 方法,校验不要使用自定义的 resultHandler 。代码如下:

      // DefaultResultSetHandler.java

      protected void checkResultHandler()
      // configuration.isSafeResultHandlerEnabled() 默认为 false
      if (resultHandler != null && configuration.isSafeResultHandlerEnabled() && !mappedStatement.isResultOrdered())
      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.");


      • 简单看看即可。
    • <1.3> 处,调用 #handleRowValuesForSimpleResultMap(...) 方法,处理嵌套映射的结果。详细解析,见 「3.1.2.3 handleRowValuesForNestedResultMap 嵌套映射」 。

3.1.2.3 handleRowValuesForSimpleResultMap 简单映射

#handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) 方法,处理简单映射的结果。代码如下:

// DefaultResultSetHandler.java

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException
// <1> 创建 DefaultResultContext 对象
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
// <2> 获得 ResultSet 对象,并跳到 rowBounds 指定的开始位置
ResultSet resultSet = rsw.getResultSet();
skipRows(resultSet, rowBounds);
// <3> 循环
while (shouldProcessMoreRows(resultContext, rowBounds) // 是否继续处理 ResultSet
&& !resultSet.isClosed() // ResultSet 是否已经关闭
&& resultSet.next()) // ResultSet 是否还有下一条
// <4> 根据该行记录以及 ResultMap.discriminator ,决定映射使用的 ResultMap 对象
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
// <5> 根据最终确定的 ResultMap 对 ResultSet 中的该行记录进行映射,得到映射后的结果对象
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
// <6> 将映射创建的结果对象添加到 ResultHandler.resultList 中保存
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);


  • <1> 处,创建 DefaultResultContext 对象。详细解析,胖友先跳到 「4. ResultContext」 中,看完就回来。
  • <2> 处,获得 ResultSet 对象,并调用 #skipRows(ResultSet rs, RowBounds rowBounds) 方法,跳到 rowBounds 指定的开始位置。代码如下:

    // DefaultResultSetHandler.java

    private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException
    if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY)
    // 直接跳转到指定开始的位置
    if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET)
    rs.absolute(rowBounds.getOffset());

    else
    // 循环,不断跳到开始的位置
    for (int i = 0; i < rowBounds.getOffset(); i++)
    if (!rs.next())
    break;




  • <3> 处,循环,满足如下三个条件。其中 #shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) 方法,是否继续处理 ResultSet 。代码如下:

    // DefaultResultSetHandler.java

    private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds)
    return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();

  • <4> 处,调用 #resolveDiscriminatedResultMap(...) 方法,根据该行记录以及 ResultMap.discriminator ,决定映射使用的 ResultMap 对象。详细解析,等下看 「3.1.2.3.1 resolveDiscriminatedResultMap」 。

  • <5> 处,调用 #getRowValue(...) 方法,根据最终确定的 ResultMap 对 ResultSet 中的该行记录进行映射,得到映射后的结果对象。详细解析,等下看 「3.1.2.3.2 getRowValue」 。
  • <6> 处,调用 #storeObject(...) 方法,将映射创建的结果对象添加到 ResultHandler.resultList 中保存。详细解析,等下看 「3.1.2.3.3 storeObject」 。
3.1.2.3.1 resolveDiscriminatedResultMap

#resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) 方法,根据该行记录以及 ResultMap.discriminator ,决定映射使用的 ResultMap 对象。代码如下:

// DefaultResultSetHandler.java

public ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) throws SQLException
// 记录已经处理过的 Discriminator 对应的 ResultMap 的编号
Set<String> pastDiscriminators = new HashSet<>();
// 如果存在 Discriminator 对象,则基于其获得 ResultMap 对象
Discriminator discriminator = resultMap.getDiscriminator();
while (discriminator != null) // 因为 Discriminator 可以嵌套 Discriminator ,所以是一个递归的过程
// 获得 Discriminator 的指定字段,在 ResultSet 中该字段的值
final Object value = getDiscriminatorValue(rs, discriminator, columnPrefix);
// 从 Discriminator 获取该值对应的 ResultMap 的编号
final String discriminatedMapId = discriminator.getMapIdFor(String.valueOf(value));
// 如果存在,则使用该 ResultMap 对象
if (configuration.hasResultMap(discriminatedMapId))
// 获得该 ResultMap 对象
resultMap = configuration.getResultMap(discriminatedMapId);
// 判断,如果出现“重复”的情况,结束循环
Discriminator lastDiscriminator = discriminator;
discriminator = resultMap.getDiscriminator();
if (discriminator == lastDiscriminator || !pastDiscriminators.add(discriminatedMapId))
break;

// 如果不存在,直接结束循环
else
break;


return resultMap;

  • 对于大多数情况下,大家不太会使用 Discriminator 的功能,此处就直接返回 resultMap ,不会执行这个很复杂的逻辑。?? 所以,如果看不太懂的胖友,可以略过这个方法,问题也不大。
  • 代码比较繁杂,胖友跟着注释看看,甚至可以调试下。其中,<1> 处,调用 #getDiscriminatorValue(ResultSet rs, Discriminator discriminator, String columnPrefix) 方法,获得 Discriminator 的指定字段,在 ResultSet 中该字段的值。代码如下:

    // DefaultResultSetHandler.java

    /**
    * 获得 ResultSet 的指定字段的值
    *
    * @param rs ResultSet 对象
    * @param discriminator Discriminator 对象
    * @param columnPrefix 字段名的前缀
    * @return 指定字段的值
    */
    private Object getDiscriminatorValue(ResultSet rs, Discriminator discriminator, String columnPrefix) throws SQLException
    final ResultMapping resultMapping = discriminator.getResultMapping();
    final TypeHandler<?> typeHandler = resultMapping.getTypeHandler();
    // 获得 ResultSet 的指定字段的值
    return typeHandler.getResult(rs, prependPrefix(resultMapping.getColumn(), columnPrefix));


    /**
    * 拼接指定字段的前缀
    *
    * @param columnName 字段的名字
    * @param prefix 前缀
    * @return prefix + columnName
    */
    private String prependPrefix(String columnName, String prefix)
    if (columnName == null || columnName.length() == 0 || prefix == null || prefix.length() == 0)
    return columnName;

    return prefix + columnName;

3.1.2.3.2 getRowValue

#getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) 方法,根据最终确定的 ResultMap 对 ResultSet 中的该行记录进行映射,得到映射后的结果对象。代码如下:

// DefaultResultSetHandler.java

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException
// <1> 创建 ResultLoaderMap 对象
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
// <2> 创建映射后的结果对象
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
// <3> 如果 hasTypeHandlerForResultObject(rsw, resultMap.getType()) 返回 true ,意味着 rowValue 是基本类型,无需执行下列逻辑。
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType()))
// <4> 创建 MetaObject 对象,用于访问 rowValue 对象
final MetaObject metaObject = configuration.newMetaObject(rowValue);
// <5> foundValues 代表,是否成功映射任一属性。若成功,则为 true ,若失败,则为 false
boolean foundValues = this.useConstructorMappings;
/// <6.1> 判断是否开启自动映射功能
if (shouldApplyAutomaticMappings(resultMap, false))
// <6.2> 自动映射未明确的列
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;

// <7> 映射 ResultMap 中明确映射的列
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
// <8> ↑↑↑ 至此,当前 ResultSet 的该行记录的数据,已经完全映射到结果对象 rowValue 的对应属性种
foundValues = lazyLoader.size() > 0 || foundValues;
// <9> 如果没有成功映射任意属性,则置空 rowValue 对象。
// 当然,如果开启 `configuration.returnInstanceForEmptyRow` 属性,则不置空。默认情况下,该值为 false
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;

return rowValue;

  • <1> 处,创建 ResultLoaderMap 对象。延迟加载相关。
  • <2> 处,调用 #createResultObject(...) 方法,创建映射后的结果对象。详细解析,见 「3.1.2.3.2.1 createResultObject」 。?? mmp ,这个逻辑的嵌套,真的太深太深了。
  • <3> 处,调用 #hasTypeHandlerForResultObject(rsw, resultMap.getType()) 方法,返回 true ,意味着 rowValue 是基本类型,无需执行下列逻辑。代码如下:

    // DefaultResultSetHandler.java

    // 判断是否结果对象是否有 TypeHandler 对象
    private boolean hasTypeHandlerForResultObject(ResultSetWrapper rsw, Class<?> resultType)
    // 如果返回的字段只有一个,则直接判断该字段是否有 TypeHandler 对象
    if (rsw.getColumnNames().size() == 1)
    return typeHandlerRegistry.hasTypeHandler(resultType, rsw.getJdbcType(rsw.getColumnNames().get(0)));

    // 判断 resultType 是否有对应的 TypeHandler 对象
    return typeHandlerRegistry.hasTypeHandler(resultType);

    • 有点绕,胖友可以调试下 BindingTest#shouldInsertAuthorWithSelectKeyAndDynamicParams() 方法。
    • 再例如,<select resultType="Integer" /> 的情况。
  • <4> 处,创建 MetaObject 对象,用于访问 rowValue 对象。
  • <5> 处,foundValues 代表,是否成功映射任一属性。若成功,则为 true ,若失败,则为 false 。另外,此处使用 useConstructorMappings 作为 foundValues 的初始值,原因是,使用了构造方法创建该结果对象,意味着一定找到了任一属性。
  • <6.1> 处,调用 #shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) 方法,判断是否使用自动映射的功能。代码如下:

    // DefaultResultSetHandler.java

    private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested)
    // 判断是否开启自动映射功能
    if (resultMap.getAutoMapping() != null)
    return resultMap.getAutoMapping();
    else
    // 内嵌查询或嵌套映射时
    if (isNested)
    return AutoMappingBehavior.FULL == configuration.getAutoMappingBehavior(); // 需要 FULL
    // 普通映射
    else
    return AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior(); // 需要 PARTIAL 或 FULL



    • org.apache.ibatis.session.AutoMappingBehavior ,自动映射行为的枚举。代码如下:

      // AutoMappingBehavior.java

      /**
      * Specifies if and how MyBatis should automatically map columns to fields/properties.
      *
      * 自动映射行为的枚举
      *
      * @author Eduardo Macarron
      */
      public enum AutoMappingBehavior

      /**
      * Disables auto-mapping.
      *
      * 禁用自动映射的功能
      */
      NONE,

      /**
      * Will only auto-map results with no nested result mappings defined inside.
      *
      * 开启部分映射的功能
      */
      PARTIAL,

      /**
      * Will auto-map result mappings of any complexity (containing nested or otherwise).
      *
      * 开启全部映射的功能
      */
      FULL


      • x
    • Configuration.autoMappingBehavior 属性,默认为 AutoMappingBehavior.PARTIAL 。
  • <6.2> 处,调用 #applyAutomaticMappings(...) 方法,自动映射未明确的列。代码有点长,所以,详细解析,见 「3.1.2.3.2.3 applyAutomaticMappings」 。

  • <7> 处,调用 #applyPropertyMappings(...) 方法,映射 ResultMap 中明确映射的列。代码有点长,所以,详细解析,见 「3.1.2.3.2.4 applyPropertyMappings」 。
  • <8> 处,↑↑↑ 至此,当前 ResultSet 的该行记录的数据,已经完全映射到结果对象 rowValue 的对应属性中。?? 整个过程,非常非常非常长,胖友耐心理解和调试下。
  • <9> 处,如果没有成功映射任意属性,则置空 rowValue 对象。当然,如果开启 configuration.returnInstanceForEmptyRow 属性,则不置空。默认情况下,该值为 false 。
3.1.2.3.2.1 createResultObject

#createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) 方法,创建映射后的结果对象。代码如下:

// DefaultResultSetHandler.java

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException
// <1> useConstructorMappings ,表示是否使用构造方法创建该结果对象。此处将其重置
this.useConstructorMappings = false; // reset previous mapping result
final List<Class<?>> constructorArgTypes = new ArrayList<>(); // 记录使用的构造方法的参数类型的数组
final List<Object> constructorArgs = new ArrayList<>(); // 记录使用的构造方法的参数值的数组
// <2> 创建映射后的结果对象
Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType()))
// <3> 如果有内嵌的查询,并且开启延迟加载,则创建结果对象的代理对象
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;



// <4> 判断是否使用构造方法创建该结果对象
this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
return resultObject;

  • <1> 处,useConstructorMappings ,表示是否使用构造方法创建该结果对象。而此处,将其重置为 false 。
  • <2> 处,调用 #createResultObject(...) 方法,创建映射后的结果对象。详细解析,往下看。?? 再次 mmp ,调用链太长了。
  • <3> 处,如果有内嵌的查询,并且开启延迟加载,则调用 ProxyFactory#createProxy(...) 方法,创建结果对象的代理对象。详细解析,见 《精尽 MyBatis 源码分析 —— SQL 执行(五)之延迟加载》 。
  • <4> 处,判断是否使用构造方法创建该结果对象,并设置到 useConstructorMappings 中。

#createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) 方法,创建映射后的结果对象。代码如下:

// DefaultResultSetHandler.java

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
throws SQLException
final Class<?> resultType = resultMap.getType();
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
// 下面,分成四种创建结果对象的情况
// <1> 情况一,如果有对应的 TypeHandler 对象,则意味着是基本类型,直接创建对结果应对象
if (hasTypeHandlerForResultObject(rsw, resultType))
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
// 情况二,如果 ResultMap 中,如果定义了 `<constructor />` 节点,则通过反射调用该构造方法,创建对应结果对象
else if (!constructorMappings.isEmpty())
return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
// 情况三,如果有默认的无参的构造方法,则使用该构造方法,创建对应结果对象
else if (resultType.isInterface() || metaType.hasDefaultConstructor())
return objectFactory.create(resultType);
// 情况四,通过自动映射的方式查找合适的构造方法,后使用该构造方法,创建对应结果对象
else if (shouldApplyAutomaticMappings(resultMap, false))
return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);

// 不支持,抛出 ExecutorException 异常
throw new ExecutorException("Do not know how to create an instance of " + resultType);

  • 分成四种创建结果对象的情况。
  • <1> 处,情况一,如果有对应的 TypeHandler 对象,则意味着是基本类型,直接创建对结果应对象。调用 #createPrimitiveResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) 方法,代码如下:

    // DefaultResultSetHandler.java

    private Object createPrimitiveResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException
    final Class<?> resultType = resultMap.getType();
    // 获得字段名
    final String columnName;
    if (!resultMap.getResultMappings().isEmpty())
    final List<ResultMapping> resultMappingList = resultMap.getResultMappings();
    final ResultMapping mapping = resultMappingList.get(0);
    columnName = prependPrefix(mapping.getColumn(), columnPrefix);
    else
    columnName = rsw.getColumnNames().get(0);

    // 获得 TypeHandler 对象
    final TypeHandler<?> typeHandler = rsw.getTypeHandler(resultType, columnName);
    // 获得 ResultSet 的指定字段的值
    return typeHandler.getResult(rsw.getResultSet(), columnName);

  • <2> 处,情况二,如果 ResultMap 中,如果定义了 <constructor /> 节点,则通过反射调用该构造方法,创建对应结果对象。调用 #createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType, List<ResultMapping> constructorMappings, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) 方法,代码如下:

    // DefaultResultSetHandler.java

    Object createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType, List<ResultMapping> constructorMappings,
    List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
    // 获得到任一的属性值。即,只要一个结果对象,有一个属性非空,就会设置为 true
    boolean foundValues = false;
    for (ResultMapping constructorMapping : constructorMappings)
    // 获得参数类型
    final Class<?> parameterType = constructorMapping.getJavaType();
    // 获得数据库的字段名
    final String column = constructorMapping.getColumn();
    // 获得属性值
    final Object value;
    try
    // 如果是内嵌的查询,则获得内嵌的值
    if (constructorMapping.getNestedQueryId() != null)
    value = getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix);
    // 如果是内嵌的 resultMap ,则递归 getRowValue 方法,获得对应的属性值
    else if (constructorMapping.getNestedResultMapId() != null)
    final ResultMap resultMap = configuration.getResultMap(constructorMapping.getNestedResultMapId());
    value = getRowValue(rsw, resultMap, constructorMapping.getColumnPrefix());
    // 最常用的情况,直接使用 TypeHandler 获取当前 ResultSet 的当前行的指定字段的值
    else
    final TypeHandler<?> typeHandler = constructorMapping.getTypeHandler();
    value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(column, columnPrefix));

    catch (ResultMapException | SQLException e)
    throw new ExecutorException("Could not process result for mapping: " + constructorMapping, e);

    // 添加到 constructorArgTypes 和 constructorArgs 中
    constructorArgTypes.add(parameterType);
    constructorArgs.add(value);
    // 判断是否获得到属性值
    foundValues = value != null || foundValues;

    // 查找 constructorArgTypes 对应的构造方法
    // 查找到后,传入 constructorArgs 作为参数,创建结果对象
    return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;

    • 代码比较简单,胖友看下注释即可。
    • 当然,里面的 #getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix) 方法的逻辑,还是略微比较复杂的。所以,我们在讲完情况三、情况四,我们再来看看它的实现。?? 写到这里,艿艿的心里无比苦闷。详细解析,见 「3.1.2.3.2.2 getNestedQueryConstructorValue」 。
  • <3> 处,情况三,如果有默认的无参的构造方法,则使用该构造方法,创建对应结果对象。
  • <4> 处,情况四,通过自动映射的方式查找合适的构造方法,后使用该构造方法,创建对应结果对象。调用 #createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) 方法,代码如下:

    // DefaultResultSetHandler.java

    private Object createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs,
    String columnPrefix) throws SQLException
    // <1> 获得所有构造方法
    final Constructor<?>[] constructors = resultType.getDeclaredConstructors();
    // <2> 获得默认构造方法
    final Constructor<?> defaultConstructor = findDefaultConstructor(constructors);
    // <3> 如果有默认构造方法,使用该构造方法,创建结果对象
    if (defaultConstructor != null)
    return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix, defaultConstructor);
    else
    // <4> 遍历所有构造方法,查找符合的构造方法,创建结果对象
    for (Constructor<?> constructor : constructors)
    if (allowedConstructorUsingTypeHandlers(constructor, rsw.getJdbcTypes()))
    return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix, constructor);



    throw new ExecutorException("No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames());

    • <1> 处,获得所有构造方法。
    • <2> 处,调用 #findDefaultConstructor(final Constructor<?>[] constructors) 方法,获得默认构造方法。代码如下:

      // DefaultResultSetHandler.java

      private Constructor<?> findDefaultConstructor(final Constructor<?>[] constructors)
      // 构造方法只有一个,直接返回
      if (constructors.length == 1) return constructors[0];
      // 获得使用 @AutomapConstructor 注解的构造方法
      for (final Constructor<?> constructor : constructors)
      if (constructor.isAnnotationPresent(AutomapConstructor.class))
      return constructor;


      return null;

      • 两种情况,比较简单。
    • <3> 处,如果有默认构造方法,调用 #createUsingConstructor(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix, Constructor<?> constructor) 方法,使用该构造方法,创建结果对象。代码如下:

      // DefaultResultSetHandler.java

      private Object createUsingConstructor(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix, Constructor<?> constructor) throws SQLException
      boolean foundValues = false;
      for (int i = 0; i < constructor.getParameterTypes().length; i++)
      // 获得参数类型
      Class<?> parameterType = constructor.getParameterTypes()[i];
      // 获得数据库的字段名
      String columnName = rsw.getColumnNames().get(i);
      // 获得 TypeHandler 对象
      TypeHandler<?> typeHandler = rsw.getTypeHandler(parameterType, columnName);
      // 获取当前 ResultSet 的当前行的指定字段的值
      Object value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(columnName, columnPrefix));
      // 添加到 constructorArgTypes 和 constructorArgs 中
      constructorArgTypes.add(parameterType);
      constructorArgs.add(value);
      // 判断是否获得到属性值
      foundValues = value != null || foundValues;

      // 查找 constructorArgTypes 对应的构造方法
      // 查找到后,传入 constructorArgs 作为参数,创建结果对象
      return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;

      • 从代码实现上,和 #createParameterizedResultObject(...) 方法,类似。
    • <4> 处,遍历所有构造方法,调用 #allowedConstructorUsingTypeHandlers(final Constructor<?> constructor, final List<JdbcType> jdbcTypes) 方法,查找符合的构造方法,后创建结果对象。代码如下:

      // DefaultResultSetHandler.java

      private boolean allowedConstructorUsingTypeHandlers(final Constructor<?> constructor, final List<JdbcType> jdbcTypes)
      final Class<?>[] parameterTypes = constructor.getParameterTypes();
      // 结果集的返回字段的数量,要和构造方法的参数数量,一致
      if (parameterTypes.length != jdbcTypes.size()) return false;
      // 每个构造方法的参数,和对应的返回字段,都要有对应的 TypeHandler 对象
      for (int i = 0; i < parameterTypes.length; i++)
      if (!typeHandlerRegistry.hasTypeHandler(parameterTypes[i], jdbcTypes.get(i)))
      return false;


      // 返回匹配
      return true;

      • 基于结果集的返回字段和构造方法的参数做比较。
3.1.2.3.2.2 getNestedQueryConstructorValue 嵌套查询

老艿艿:冲鸭!!!太冗长了!!!各种各种各种!!!!情况!!!!!

#getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix) 方法,获得嵌套查询的值。代码如下:

// DefaultResultSetHandler.java

private Object getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix) throws SQLException
// <1> 获得内嵌查询的编号
final String nestedQueryId = constructorMapping.getNestedQueryId();
// <1> 获得内嵌查询的 MappedStatement 对象
final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
// <1> 获得内嵌查询的参数类型
final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
// <2> 获得内嵌查询的参数对象
final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, constructorMapping, nestedQueryParameterType, columnPrefix);
Object value = null;
// <3> 执行查询
if (nestedQueryParameterObject != null)
// <3.1> 获得 BoundSql 对象
final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
// <3.2> 获得 CacheKey 对象
final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
final Class<?> targetType = constructorMapping.getJavaType();
// <3.3> 创建 ResultLoader 对象
final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
// <3.3> 加载结果
value = resultLoader.loadResult();

return value;

  • 关于这个方法,胖友可以调试 BaseExecutorTest#shouldFetchOneOrphanedPostWithNoBlog() 这个单元测试方法。
  • <1> 处,获得内嵌查询的编号、MappedStatement 对象、参数类型。
  • <2> 处,调用 #prepareParameterForNestedQuery(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) 方法,获得内嵌查询的参数对象。代码如下:

    // DefaultResultSetHandler.java

    // 获得内嵌查询的参数类型
    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);



    // ① 获得普通类型的内嵌查询的参数对象
    private Object prepareSimpleKeyParameter(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) throws SQLException
    // 获得 TypeHandler 对象
    final TypeHandler<?> typeHandler;
    if (typeHandlerRegistry.hasTypeHandler(parameterType))
    typeHandler = typeHandlerRegistry.getTypeHandler(parameterType);
    else
    typeHandler = typeHandlerRegistry.getUnknownTypeHandler();

    // 获得指定字段的值
    return typeHandler.getResult(rs, prependPrefix(resultMapping.getColumn(), columnPrefix));


    // ② 获得组合类型的内嵌查询的参数对象
    private Object prepareCompositeKeyParameter(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) throws SQLException
    // 创建参数对象
    final Object parameterObject = instantiateParameterObject(parameterType);
    // 创建参数对象的 MetaObject 对象,可对其进行访问
    final MetaObject metaObject = configuration.newMetaObject(parameterObject);
    boolean foundValues = false;
    // 遍历组合的所有字段
    for (ResultMapping innerResultMapping : resultMapping.getComposites())
    // 获得属性类型
    final Class<?> propType = metaObject.getSetterType(innerResultMapping.getProperty());
    // 获得对应的 TypeHandler 对象
    final TypeHandler<?> typeHandler = typeHandlerRegistry.getTypeHandler(propType);
    // 获得指定字段的值
    final Object propValue = typeHandler.getResult(rs, prependPrefix(innerResultMapping.getColumn(), columnPrefix));
    // issue #353 & #560 do not execute nested query if key is null
    // 设置到 parameterObject 中,通过 metaObject
    if (propValue != null)
    metaObject.setValue(innerResultMapping.getProperty(), propValue);
    foundValues = true; // 标记 parameterObject 非空对象


    // 返回参数对象
    return foundValues ? parameterObject : null;


    // ② 创建参数对象
    private Object instantiateParameterObject(Class<?> parameterType)
    if (parameterType == null)
    return new HashMap<>();
    else if (ParamMap.class.equals(parameterType))
    return new HashMap<>(); // issue #649
    else
    return objectFactory.create(parameterType);


    • 虽然代码比较长,但是非常简单。注意下,艿艿添加了  和  两个序号,分别对应两种情况。
  • <3> 处,整体是,执行查询,获得值。
3.1.2.3.2.3 applyAutomaticMappings

#createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) 方法,创建映射后的结果对象。代码如下:

// DefaultResultSetHandler.java

private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException
// <1> 获得 UnMappedColumnAutoMapping 数组
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (!autoMapping.isEmpty())
// <2> 遍历 UnMappedColumnAutoMapping 数组
for (UnMappedColumnAutoMapping mapping : autoMapping)
// 获得指定字段的值
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
// 若非空,标记 foundValues 有值
if (value != null)
foundValues = true;

// 设置到 parameterObject 中,通过 metaObject
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;

  • <1> 处,调用 #createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) 方法,获得 UnMappedColumnAutoMapping 数组。代码如下:

    // DefaultResultSetHandler.java

    private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException
    // 生成 autoMappingsCache 的 KEY
    final String mapKey = resultMap.getId() + ":" + columnPrefix;
    // 从缓存 autoMappingsCache 中,获得 UnMappedColumnAutoMapping 数组
    List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
    // 如果获取不到,则进行初始化
    if (autoMapping == null)
    autoMapping = new ArrayList<>();
    // 获得未 mapped 的字段的名字的数组
    final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
    // 遍历 unmappedColumnNames 数组
    for (String columnName : unmappedColumnNames)
    // 获得属性名
    String propertyName = columnName;
    if (columnPrefix != null && !columnPrefix.isEmpty())
    // When columnPrefix is specified,
    // ignore columns without the prefix.
    if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix))
    propertyName = columnName.substring(columnPrefix.length());
    else
    continue;


    // 从结果对象的 metaObject 中,获得对应的属性名
    final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
    // 获得到属性名,并且可以进行设置
    if (property != null && metaObject.hasSetter(property))
    // 排除已映射的属性
    if (resultMap.getMappedProperties().contains(property))
    continue;

    // 获得属性的类型
    final Class<?> propertyType = metaObject.getSetterType(property);
    // 判断是否有对应的 TypeHandler 对象。如果有,则创建 UnMappedColumnAutoMapping 对象,并添加到 autoMapping 中
    if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName)))
    final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
    autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
    // 如果没有,则执行 AutoMappingUnknownColumnBehavior 对应的逻辑
    else
    configuration.getAutoMappingUnknownColumnBehavior()
    .doAction(mappedStatement, columnName, property, propertyType);

    // 如果没有属性,或者无法设置,则则执行 AutoMappingUnknownColumnBehavior 对应的逻辑
    else
    configuration.getAutoMappingUnknownColumnBehavior()
    .doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);


    // 添加到缓存中
    autoMappingsCache.put(mapKey, autoMapping);

    return autoMapping;

    • 虽然代码比较长,但是逻辑很简单。遍历未 mapped 的字段的名字的数组,映射每一个字段在结果对象的相同名字的属性,最终生成 UnMappedColumnAutoMapping 对象。
    • UnMappedColumnAutoMapping ,是 DefaultResultSetHandler 的内部静态类,未 mapped 字段自动映射后的对象。代码如下:

      // DefaultResultSetHandler.java

      private static class UnMappedColumnAutoMapping

      /**
      * 字段名
      */
      private final String column;
      /**
      * 属性名
      */
      private final String property;
      /**
      * TypeHandler 处理器
      */
      private final TypeHandler<?> typeHandler;
      /**
      * 是否为基本属性
      */
      private final boolean primitive;

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



      • x
    • 当找不到映射的属性时,会调用 AutoMappingUnknownColumnBehavior#doAction(MappedStatement mappedStatement, String columnName, String propertyName, Class<?> propertyType) 方法,执行相应的逻辑。比较简单,胖友直接看 org.apache.ibatis.session.AutoMappingUnknownColumnBehavior 即可。
    • Configuration.autoMappingUnknownColumnBehavior 为 AutoMappingUnknownColumnBehavior.NONE ,即不处理。
  • <2> 处,遍历 UnMappedColumnAutoMapping 数组,获得指定字段的值,设置到 parameterObject 中,通过 metaObject 。
3.1.2.3.2.4 applyPropertyMappings

#applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix) 方法,映射 ResultMap 中明确映射的列。代码如下:

// DefaultResultSetHandler.java

private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException
// 获得 mapped 的字段的名字的数组
final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
// 遍历 ResultMapping 数组
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings)
// 获得字段名
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
if (propertyMapping.getNestedResultMapId() != null)
// the user added a column attribute to a nested result map, ignore it
column = null;

if (propertyMapping.isCompositeResult() // 组合
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) // 属于 mappedColumnNames
|| propertyMapping.getResultSet() != null) // 存储过程
// <1> 获得指定字段的值
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
// issue #541 make property optional
final String property = propertyMapping.getProperty();
if (property == null)
continue;
// 存储过程相关,忽略
else if (value == DEFERED)
foundValues = true;
continue;

// 标记获取到任一属性
if (value != null)
foundValues = true;

// 设置到 parameterObject 中,通过 metaObject
if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive()))
// gcode issue #377, call setter on nulls (value is not ‘found‘)
metaObject.setValue(property, value);



return foundValues;

  • 虽然代码比较长,但是逻辑很简单。胖友自己瞅瞅。
  • 在 <1> 处,调用 #getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) 方法,获得指定字段的值。代码如下:

    // DefaultResultSetHandler.java

    private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
    throws SQLException
    // <2> 内嵌查询,获得嵌套查询的值
    if (propertyMapping.getNestedQueryId() != null)
    return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
    // 存储过程相关,忽略
    else if (propertyMapping.getResultSet() != null)
    addPendingChildRelation(rs, metaResultObject, propertyMapping); // TODO is that OK?
    return DEFERED;
    // 普通,直接获得指定字段的值
    else
    final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
    final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
    return typeHandler.getResult(rs, column);


    • 在 <2> 处,我们又碰到了一个内嵌查询,调用 #getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) 方法,获得嵌套查询的值。详细解析,见 「」 。
3.1.2.3.2.5 getNestedQueryMappingValue 嵌套查询

#getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) 方法,获得嵌套查询的值。代码如下:

// DefaultResultSetHandler.java

private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException
// 获得内嵌查询的编号
final String nestedQueryId = propertyMapping.getNestedQueryId();
// 获得属性名
final String property = propertyMapping.getProperty();
// 获得内嵌查询的 MappedStatement 对象
final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
// 获得内嵌查询的参数类型
final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
// 获得内嵌查询的参数对象
final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, propertyMapping, nestedQueryParameterType, columnPrefix);
Object value = null;
if (nestedQueryParameterObject != null)
// 获得 BoundSql 对象
final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
// 获得 CacheKey 对象
final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
final Class<?> targetType = propertyMapping.getJavaType();
// <1> 检查缓存中已存在
if (executor.isCached(nestedQuery, key)) // 有缓存
// <2.1> 创建 DeferredLoad 对象,并通过该 DeferredLoad 对象从缓存中加载结采对象
executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
// <2.2> 返回已定义
value = DEFERED;
// 检查缓存中不存在
else // 无缓存
// <3.1> 创建 ResultLoader 对象
final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
// <3.2> 如果要求延迟加载,则延迟加载
if (propertyMapping.isLazy())
// 如果该属性配置了延迟加载,则将其添加到 `ResultLoader.loaderMap` 中,等待真正使用时再执行嵌套查询并得到结果对象。
lazyLoader.addLoader(property, metaResultObject, resultLoader);
// 返回已定义
value = DEFERED;
// <3.3> 如果不要求延迟加载,则直接执行加载对应的值
else
value = resultLoader.loadResult();



return value;

  • 和 「3.1.2.3.2.2 getNestedQueryConstructorValue」 一样,也是嵌套查询。所以,从整体代码的实现上,也是非常类似的。差别在于:
    • #getNestedQueryConstructorValue(...) 方法,用于构造方法需要用到的嵌套查询的值,它是不用考虑延迟加载的。
    • #getNestedQueryMappingValue(...) 方法,用于 setting 方法需要用到的嵌套查询的值,它是需要考虑延迟加载的。
  • <1> 处,调用 Executor#isCached(MappedStatement ms, CacheKey key) 方法,检查缓存中已存在。下面,我们分成两种情况来解析。
  • ========== 有缓存 ==========
  • <2.1> 处,调用 Executor#deferLoad(...) 方法,创建 DeferredLoad 对象,并通过该 DeferredLoad 对象从缓存中加载结采对象。详细解析,见 《精尽 MyBatis 源码分析 —— SQL 执行(五)之延迟加载》 。
  • <2.2> 处,返回已定义 DEFERED 。
  • ========== 有缓存 ==========
  • <3.1> 处,创建 ResultLoader 对象。
  • <3.2> 处,如果要求延迟加载,则延迟加载。
  • <3.3> 处,如果不要求延迟加载,则调用 ResultLoader#loadResult() 方法,直接执行加载对应的值。
3.1.2.3.3 storeObject

#storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) 方法,将映射创建的结果对象添加到 ResultHandler.resultList 中保存。代码如下:

// DefaultResultSetHandler.java

private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException
// 暂时忽略,这个情况,只有存储过程会出现
if (parentMapping != null)
linkToParents(rs, parentMapping, rowValue);
else
callResultHandler(resultHandler, resultContext, rowValue);



@SuppressWarnings("unchecked" /* because ResultHandler<?> is always ResultHandler<Object>*/)
// 调用 ResultHandler ,进行结果的处理
private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue)
// 设置结果对象到 resultContext 中
resultContext.nextResultObject(rowValue);
// <x> 使用 ResultHandler 处理结果。
// 如果使用 DefaultResultHandler 实现类的情况,会将映射创建的结果对象添加到 ResultHandler.resultList 中保存
((ResultHandler<Object>) resultHandler).handleResult(resultContext);

  • 逻辑比较简单,认真看下注释,特别是 <x> 处。

3.1.2.4 handleRowValuesForNestedResultMap 嵌套映射

可能胖友对嵌套映射的概念不是很熟悉,胖友可以调试 AncestorRefTest#testAncestorRef() 这个单元测试方法。


老艿艿:本小节,还是建议看 《MyBatis 技术内幕》 的 「3.3.4 嵌套映射」 小节。因为,它提供了比较好的这块逻辑的原理讲解,并且配置了大量的图。

?? 精力有限,后续补充哈。
?? 实际是,因为艿艿比较少用嵌套映射,所以对这块逻辑,不是很感兴趣。

#handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) 方法,处理嵌套映射的结果。代码如下:

// DefaultResultSetHandler.java

3.1.3 handleCursorResultSets

#handleCursorResultSets(Statement stmt) 方法,处理 java.sql.ResultSet 成 Cursor 对象。代码如下:

// DefaultResultSetHandler.java

@Override
public <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException
ErrorContext.instance().activity("handling cursor results").object(mappedStatement.getId());

// 获得首个 ResultSet 对象,并封装成 ResultSetWrapper 对象
ResultSetWrapper rsw = getFirstResultSet(stmt);

// 游标方式的查询,只允许一个 ResultSet 对象。因此,resultMaps 数组的数量,元素只能有一个
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
if (resultMapCount != 1)
throw new ExecutorException("Cursor results cannot be mapped to multiple resultMaps");


// 获得 ResultMap 对象,后创建 DefaultCursor 对象
ResultMap resultMap = resultMaps.get(0);
return new DefaultCursor<>(this, resultMap, rsw, rowBounds);

4. ResultContext

老艿艿:这个类,大体看看每个方法的用途,结合上文一起理解即可。

org.apache.ibatis.session.ResultContext ,结果上下文接口。代码如下:

// ResultContext.java

public interface ResultContext<T>

/**
* @return 当前结果对象
*/
T getResultObject();

/**
* @return 总的结果对象的数量
*/
int getResultCount();

/**
* @return 是否暂停
*/
boolean isStopped();

/**
* 暂停
*/
void stop();


4.1 DefaultResultContext

org.apache.ibatis.executor.result.DefaultResultContext ,实现 ResultContext 接口,默认的 ResultContext 的实现类。代码如下:

// DefaultResultContext.java

public class DefaultResultContext<T> implements ResultContext<T>

/**
* 当前结果对象
*/
private T resultObject;
/**
* 总的结果对象的数量
*/
private int resultCount;
/**
* 是否暂停
*/
private boolean stopped;

public DefaultResultContext()
resultObject = null;
resultCount = 0;
stopped = false; // 默认非暂停


@Override
public T getResultObject()
return resultObject;


@Override
public int getResultCount()
return resultCount;


@Override
public boolean isStopped()
return stopped;


/**
* 当前结果对象
*
* @param resultObject 当前结果对象
*/
public void nextResultObject(T resultObject)
// 数量 + 1
resultCount++;
// 当前结果对象
this.resultObject = resultObject;


@Override
public void stop()
this.stopped = true;



5. ResultHandler

org.apache.ibatis.session.ResultHandler ,结果处理器接口。代码如下:

// ResultHandler.java

public interface ResultHandler<T>

/**
* 处理当前结果
*
* @param resultContext ResultContext 对象。在其中,可以获得当前结果
*/
void handleResult(ResultContext<? extends T> resultContext);


5.1 DefaultResultHandler

org.apache.ibatis.executor.result.DefaultResultHandler ,实现 ResultHandler 接口,默认的 ResultHandler 的实现类。代码如下:

// DefaultResultHandler.java

public class DefaultResultHandler implements ResultHandler<Object>

/**
* 结果数组
*/
private final List<Object> list;

public DefaultResultHandler()
list = new ArrayList<>();


@SuppressWarnings("unchecked")
public DefaultResultHandler(ObjectFactory objectFactory)
list = objectFactory.create(List.class);


@Override
public void handleResult(ResultContext<? extends Object> context)
// <1> 将当前结果,添加到结果数组中
list.add(context.getResultObject());


public List<Object> getResultList()
return list;



  • 核心代码就是 <1> 处,将当前结果,添加到结果数组中。

5.2 DefaultMapResultHandler

该类在 session 包中实现,我们放在会话模块的文章中,详细解析。

6. Cursor

org.apache.ibatis.cursor.Cursor ,继承 Closeable、Iterable 接口,游标接口。代码如下:

// Cursor.java

public interface Cursor<T> extends Closeable, Iterable<T>

/**
* 是否处于打开状态
*
* @return true if the cursor has started to fetch items from database.
*/
boolean isOpen();

/**
* 是否全部消费完成
*
* @return true if the cursor is fully consumed and has returned all elements matching the query.
*/
boolean isConsumed();

/**
* 获得当前索引
*
* Get the current item index. The first item has the index 0.
* @return -1 if the first cursor item has not been retrieved. The index of the current item retrieved.
*/
int getCurrentIndex();


6.1 DefaultCursor

org.apache.ibatis.cursor.defaults.DefaultCursor ,实现 Cursor 接口,默认 Cursor 实现类。

6.1.1 构造方法

// DefaultCursor.java

// ResultSetHandler stuff
private final DefaultResultSetHandler resultSetHandler;
private final ResultMap resultMap;
private final ResultSetWrapper rsw;
private final RowBounds rowBounds;
/**
* ObjectWrapperResultHandler 对象
*/
private final ObjectWrapperResultHandler<T> objectWrapperResultHandler = new ObjectWrapperResultHandler<>();

/**
* CursorIterator 对象,游标迭代器。
*/
private final CursorIterator cursorIterator = new CursorIterator();
/**
* 是否开始迭代
*
* @link #iterator()
*/
private boolean iteratorRetrieved;
/**
* 游标状态
*/
private CursorStatus status = CursorStatus.CREATED;
/**
* 已完成映射的行数
*/
private int indexWithRowBound = -1;

public DefaultCursor(DefaultResultSetHandler resultSetHandler, ResultMap resultMap, ResultSetWrapper rsw, RowBounds rowBounds)
this.resultSetHandler = resultSetHandler;
this.resultMap = resultMap;
this.rsw = rsw;
this.rowBounds = rowBounds;

  • 大体瞄下每个属性的意思。下面,每个方法,胖友会更好的理解每个属性。

6.1.2 CursorStatus

CursorStatus ,是 DefaultCursor 的内部枚举类。代码如下:

// DefaultCursor.java

private enum CursorStatus

/**
* A freshly created cursor, database ResultSet consuming has not started
*/
CREATED,
/**
* A cursor currently in use, database ResultSet consuming has started
*/
OPEN,
/**
* A closed cursor, not fully consumed
*
* 已关闭,并未完全消费
*/
CLOSED,
/**
* A fully consumed cursor, a consumed cursor is always closed
*
* 已关闭,并且完全消费
*/
CONSUMED

6.1.3 isOpen

// DefaultCursor.java

@Override
public boolean isOpen()
return status == CursorStatus.OPEN;

6.1.4 isConsumed

// DefaultCursor.java

@Override
public boolean isConsumed()
return status == CursorStatus.CONSUMED;

6.1.5 iterator

#iterator() 方法,获取迭代器。代码如下:

// DefaultCursor.java

@Override
public Iterator<T> iterator()
// 如果已经获取,则抛出 IllegalStateException 异常
if (iteratorRetrieved)
throw new IllegalStateException("Cannot open more than one iterator on a Cursor");

if (isClosed())
throw new IllegalStateException("A Cursor is already closed.");

// 标记已经获取
iteratorRetrieved = true;
return cursorIterator;

  • 通过 iteratorRetrieved 属性,保证有且仅返回一次 cursorIterator 对象。

6.1.6 ObjectWrapperResultHandler

ObjectWrapperResultHandler ,DefaultCursor 的内部静态类,实现 ResultHandler 接口,代码如下:

// DefaultCursor.java

private static class ObjectWrapperResultHandler<T> implements ResultHandler<T>

/**
* 结果对象
*/
private T result;

@Override
public void handleResult(ResultContext<? extends T> context)
// <1> 设置结果对象
this.result = context.getResultObject();
// <2> 暂停
context.stop();



  • <1> 处,暂存 「3.1 DefaultResultSetHandler」 处理的 ResultSet 的当前行的结果。
  • <2> 处,通过调用 ResultContext#stop() 方法,暂停 DefaultResultSetHandler 在向下遍历下一条记录,从而实现每次在调用 CursorIterator#hasNext() 方法,只遍历一行 ResultSet 的记录。如果胖友有点懵逼,可以在看看 DefaultResultSetHandler#shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) 方法,代码如下:

    // DefaultCursor.java

    private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds)
    return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();

6.1.7 CursorIterator

CursorIterator ,DefaultCursor 的内部类,实现 Iterator 接口,游标的迭代器实现类。代码如下:

// DefaultCursor.java

private class CursorIterator implements Iterator<T>

/**
* Holder for the next object to be returned
*
* 结果对象,提供给 @link #next() 返回
*/
T object;

/**
* Index of objects returned using next(), and as such, visible to users.
* 索引位置
*/
int iteratorIndex = -1;

@Override
public boolean hasNext()
// <1> 如果 object 为空,则遍历下一条记录
if (object == null)
object = fetchNextUsingRowBound();

// <2> 判断 object 是否非空
return object != null;


@Override
public T next()
// <3> Fill next with object fetched from hasNext()
T next = object;

// <4> 如果 next 为空,则遍历下一条记录
if (next == null)
next = fetchNextUsingRowBound();


// <5> 如果 next 非空,说明有记录,则进行返回
if (next != null)
// <5.1> 置空 object 对象
object = null;
// <5.2> 增加 iteratorIndex
iteratorIndex++;
// <5.3> 返回 next
return next;


// <6> 如果 next 为空,说明没有记录,抛出 NoSuchElementException 异常
throw new NoSuchElementException();


@Override
public void remove()
throw new UnsupportedOperationException("Cannot remove element from Cursor");



  • #hasNext() 方法,判断是否有下一个结果对象:
    • <1> 处,如果 object 为空,则调用 #fetchNextUsingRowBound() 方法,遍历下一条记录。也就是说,该方法是先获取下一条记录,然后在判断是否存在下一条记录。实际上,和 java.util.ResultSet 的方式,是一致的。如果再调用一次该方法,则不会去遍历下一条记录。关于 #fetchNextUsingRowBound() 方法,详细解析,见 「6.1.8 fetchNextUsingRowBound」 。
    • <2> 处,判断 object 非空。
  • #next() 方法,获得下一个结果对象:
    • <3> 处,先记录 object 到 next 中。为什么要这么做呢?继续往下看。
    • <4> 处,如果 next 为空,有两种可能性:1)使用方未调用 #hasNext() 方法;2)调用 #hasNext() 方法,发现没下一条,还是调用了 #next() 方法。如果 next() 方法为空,通过“再次”调用 #fetchNextUsingRowBound() 方法,去遍历下一条记录。
    • <5> 处,如果 next 非空,说明有记录,则进行返回。
      • <5.1> 处,置空 object 对象。
      • <5.2> 处,增加 iteratorIndex 。
      • <5.3> 处,返回 next 。如果 <3> 处,不进行 next 的赋值,如果 <5.1> 处的置空,此处就无法返回 next 了。
    • <6> 处,如果 next 为空,说明没有记录,抛出 NoSuchElementException 异常。

6.1.8 fetchNextUsingRowBound

#fetchNextUsingRowBound() 方法,遍历下一条记录。代码如下:

// DefaultCursor.java

protected T fetchNextUsingRowBound()
// <1> 遍历下一条记录
T result = fetchNextObjectFromDatabase();
// 循环跳过 rowBounds 的索引
while (result != null && indexWithRowBound < rowBounds.getOffset())
result = fetchNextObjectFromDatabase();

// 返回记录
return result;

  • <1> 处,调用 #fetchNextObjectFromDatabase() 方法,遍历下一条记录。代码如下:
// DefaultCursor.java

protected T fetchNextObjectFromDatabase()
// <1> 如果已经关闭,返回 null
if (isClosed())
return null;


try
// <2> 设置状态为 CursorStatus.OPEN
status = CursorStatus.OPEN;
// <3> 遍历下一条记录
if (!rsw.getResultSet().isClosed())
resultSetHandler.handleRowValues(rsw, resultMap, objectWrapperResultHandler, RowBounds.DEFAULT, null);

catch (SQLException e)
throw new RuntimeException(e);


// <4> 复制给 next
T next = objectWrapperResultHandler.result;
// <5> 增加 indexWithRowBound
if (next != null)
indexWithRowBound++;

// No more object or limit reached
// <6> 没有更多记录,或者到达 rowBounds 的限制索引位置,则关闭游标,并设置状态为 CursorStatus.CONSUMED
if (next == null || getReadItemsCount() == rowBounds.getOffset() + rowBounds.getLimit())
close();
status = CursorStatus.CONSUMED;

// <7> 置空 objectWrapperResultHandler.result 属性
objectWrapperResultHandler.result = null;
// <8> 返回下一条结果
return next;

  • <1> 处,调用 #isClosed() 方法,判断是否已经关闭。若是,则返回 null 。代码如下:

    // DefaultCursor.java

    private boolean isClosed()
    return status == CursorStatus.CLOSED || status == CursorStatus.CONSUMED;

  • <2> 处,设置状态为 CursorStatus.OPEN 。

  • <3> 处,调用 DefaultResultSetHandler#handleRowValues(...) 方法,遍历下一条记录。也就是说,回到了 [「3.1.2.2 handleRowValues」 的流程。遍历的下一条件记录,会暂存到 objectWrapperResultHandler.result 中。
  • <4> 处,复制给 next 。
  • <5> 处,增加 indexWithRowBound 。
  • <6> 处,没有更多记录,或者到达 rowBounds 的限制索引位置,则关闭游标,并设置状态为 CursorStatus.CONSUMED 。其中,涉及到的方法,代码如下:

    // DefaultCursor.java

    private int getReadItemsCount()
    return indexWithRowBound + 1;


    @Override
    public void close()
    if (isClosed())
    return;


    // 关闭 ResultSet
    ResultSet rs = rsw.getResultSet();
    try
    if (rs != null)
    rs.close();

    catch (SQLException e)
    // ignore
    finally
    // 设置状态为 CursorStatus.CLOSED
    status = CursorStatus.CLOSED;


  • <7> 处,置空 objectWrapperResultHandler.result 属性。
  • <8> 处,返回 next 。

以上是关于mybatis-ResultSetHandler的主要内容,如果未能解决你的问题,请参考以下文章