Mybatis源码解析之DefaultResultSetHandler的handleResultSets方法解析

 

ResultSetHandler是Mybatis的核心组件,主要负责将结果集resultSets转化成结果列表(或cursor)和处理储存过程的输出。
DefaultResultSetHandler是Myabtis为ResultSetHandler提供的唯一一个实现类,下面我们将深入DefaultResultSetHandler的源码了解其实如何转化结果集resultSet的。

一、ResultSetHandler

public interface ResultSetHandler {

 //将结果集转化成list
  <E> List<E> handleResultSets(Statement stmt) throws SQLException;

//将结果集转化出呢个cursor
  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

//处理存储过程的输出
  void handleOutputParameters(CallableStatement cs) throws SQLException;

}

二、handleResultSets方法解析

1. DefaultResultSetHandler#handleResultSets(Statement)

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

   final List<Object> multipleResults = new ArrayList<Object>();

   int resultSetCount = 0;
   //结果集的第一个结果
   ResultSetWrapper rsw = getFirstResultSet(stmt);

   List<ResultMap> resultMaps = mappedStatement.getResultMaps();
   int resultMapCount = resultMaps.size();
   validateResultMapsCount(rsw, resultMapCount);
   while (rsw != null && resultMapCount > resultSetCount) {
     ResultMap resultMap = resultMaps.get(resultSetCount);
     //根据resultMap处理rsw生成java对象
     handleResultSet(rsw, resultMap, multipleResults, null);
     //获取结果集的下一个结果
     rsw = getNextResultSet(stmt);
     cleanUpAfterHandlingResultSet();
     resultSetCount++;
   }

   String[] resultSets = mappedStatement.getResultSets();
   if (resultSets != null) {
   	//和resultMaps的遍历处理类似
     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++;
     }
   }

   return collapseSingleResultList(multipleResults);
 }
private void cleanUpAfterHandlingResultSet() {
  nestedResultObjects.clear();
}
//当list长度为1,表示该list的元素就是list类型的,返回元素即可。
@SuppressWarnings("unchecked")
private List<Object> collapseSingleResultList(List<Object> multipleResults) {
  return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
}

一般情况下,mappedStament中也只有一个resultMap,并不需要遍历。
额外提一点,即使我们在select的查询中配置的不是resultMap而是resultType,在mybatis中也是按照resultMap进行存储的。具体代码在MapperStatementAssistant#getSatementResultMaps(String, String, Class<?>, String)方法中,具体涉及到的代码如下:

else if (resultType != null) {
  ResultMap inlineResultMap = new ResultMap.Builder(
      configuration,
      statementId + "-Inline",
      resultType,
      new ArrayList<ResultMapping>(),
      null).build();
  resultMaps.add(inlineResultMap);
}

2. DefaultResultSetHandler#handleResultSet(ResultSetWrapper, ResultMap, List, ResultMapping)

ResultSet处理

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
  try {
    if (parentMapping != null) {
      handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
    } else {
      if (resultHandler == null) {
        DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
        handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
        multipleResults.add(defaultResultHandler.getResultList());
      } else {
        handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
      }
    }
  } finally {
    // issue #228 (close resultsets)
    closeResultSet(rsw.getResultSet());
  }
}

可以看到,虽然按照parentMapping和resultHandler分成了3种情况,但最终都进入了handleRowValues方法。

3. DefaultResultSetHandler#handleRowValue(ResultSetWrapper, ResultMap, ResultHandler, RowBounds, ResultMapping)

区分嵌套和不嵌套的处理

public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
  if (resultMap.hasNestedResultMaps()) {
    ensureNoRowBounds();
    checkResultHandler();
    handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
  } else {
    handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
  }
}

可以看到,分成嵌套和不嵌套两种方法,进行处理,这里我们只管理不嵌套的处理,嵌套的虽然会比不嵌套复杂一点,但总体类似,差别并不大。

4. DefaultResultSetHandler#handleRowValueForSimpleMap(ResultSetWrapper, ResultMap, ResultHandler, RowBounds, ResultMapping)

分页处理并确定resultMap

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
    throws SQLException {
  DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
  //
  skipRows(rsw.getResultSet(), rowBounds);
  while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
  //根据discriminate找到适合的ResultMap
    ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
    Object rowValue = getRowValue(rsw, discriminatedResultMap);
    storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
  }
}

//游标滑动,实现分页效果
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++) {
      rs.next();
    }
  }
}

private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
 if (parentMapping != null) {
    linkToParents(rs, parentMapping, rowValue);
  } else {
    callResultHandler(resultHandler, resultContext, rowValue);
  }
}
private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
  resultContext.nextResultObject(rowValue);
  ((ResultHandler<Object>) resultHandler).handleResult(resultContext);
}

可以看出来核心逻辑实在Object rowValue = getRowValue(rsw, discriminatedResultMap);中。

5. DefaultResultSetHandler#getRowValue(ResultSetWrapper, ResultMap)

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
  final ResultLoaderMap lazyLoader = new ResultLoaderMap();
  Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
  if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    final MetaObject metaObject = configuration.newMetaObject(rowValue);
    boolean foundValues = this.useConstructorMappings;
    if (shouldApplyAutomaticMappings(resultMap, false)) {
      foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
    }
    foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
    foundValues = lazyLoader.size() > 0 || foundValues;
    rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;
  }
  return rowValue;
}

该方法生成了java对象,分3步
(1)createResultObject 生成对象
(2)applyAutomaticMappings 自动映射
(3)applyPropertyMappings property映射
这里还附带了ResultLoaderMap 的懒加载处理。

6 DefaultResultSetHandler#createResultObject(ResultSetWrapper, ResultMap, ResultLoadMap, String columnPrefix)

生成对象并处理嵌套查询的懒加载属性。懒加载本篇文章暂不分析。

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
  this.useConstructorMappings = false; // reset previous mapping result
  final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
  final List<Object> constructorArgs = new ArrayList<Object>();
  //生成对象
  Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
  if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
    for (ResultMapping propertyMapping : propertyMappings) {
      // issue gcode #109 && issue #149
      //属性嵌套查询且懒加载
      if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
        resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
        break;
      }
    }
  }
  this.useConstructorMappings = (resultObject != null && !constructorArgTypes.isEmpty()); // set current mapping result
  return resultObject;
}

7 DefaultResultSetHandler#createResultObject(ResultSetWrapper, ResultMap, List<Class<?>>, List)

生成对象

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();
  if (hasTypeHandlerForResultObject(rsw, resultType)) {
  //存在适用的typeHanlder类,事实上一般为基本数据类型或者其封装类
    return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
  } else if (!constructorMappings.isEmpty()) {
  //有参构造函数的constructor映射
    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);
  }
  throw new ExecutorException("Do not know how to create an instance of " + resultType);
}

8. DefaultResultSetHandler#applyAutomaticMappings(ResultSetWrapper, ResultMap, MetaObject, String)

自动映射

private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
//创建自动映射的映射对
  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;
      }
      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;
}

在启用自动映射的前提下,进行自动映射。
判断是否开启自动映射的条件:

private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) {
//resultMap配置了autoMapping = true
  if (resultMap.getAutoMapping() != null) {
    return resultMap.getAutoMapping();
  } else {
  //xml setting的属性autoMappingBehavior,有3个值:NONE(不启用),PARTIAL(不嵌套的时候启动),FULL(启动)
    if (isNested) {
      return AutoMappingBehavior.FULL == configuration.getAutoMappingBehavior();
    } else {
      return AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior();
    }
  }
}

9. DefaultResultSetHandler#createAutomaticMappings(ResultSetWrapper, ResultMap, MetaObject, String)

找到自动映射对

private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
  final String mapKey = resultMap.getId() + ":" + columnPrefix;
  //autoMappingsCache作为缓存,首先从缓存中获取
  List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
  //缓存未命中
  if (autoMapping == null) {
    autoMapping = new ArrayList<UnMappedColumnAutoMapping>();
    final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
    for (String columnName : unmappedColumnNames) {
      String propertyName = columnName;
      if (columnPrefix != null && !columnPrefix.isEmpty()) {
        // When columnPrefix is specified,
        // ignore columns without the prefix.
        if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
          propertyName = columnName.substring(columnPrefix.length());
        } else {
          continue;
        }
      }
      //驼峰
      final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
      //存在set方法
      if (property != null && metaObject.hasSetter(property)) {
      //resultMap的应映射中已存在,忽略
        if (resultMap.getMappedProperties().contains(property)) {
          continue;
        }
        final Class<?> propertyType = metaObject.getSetterType(property);
        if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
          final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
          autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
        } else {
        //没找到,根据autoMappingUnknownColumnBehavior属性(默认为NONE)进行处理:NONE(忽略),WARNING(log.warn),ERROR(抛异常)
          configuration.getAutoMappingUnknownColumnBehavior()
              .doAction(mappedStatement, columnName, property, propertyType);
        }
      } else {
      // //没找到,根据autoMappingUnknownColumnBehavior属性(默认为NONE)进行处理:NONE(忽略),WARNING(log.warn),ERROR(抛异常)
        configuration.getAutoMappingUnknownColumnBehavior()
            .doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
      }
    }
    //加入缓存
    autoMappingsCache.put(mapKey, autoMapping);
  }
  return autoMapping;
}

10. DefaultResultSetHandler#applyPropertyMappings(ResultSetWrapper, ResultMap, MetaObject, ResultLoaderMap, String)

resultmap的property映射

private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
    throws SQLException {
  final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
  boolean foundValues = false;
  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 
      column = null;
    }
    if (propertyMapping.isCompositeResult()
        || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
        || propertyMapping.getResultSet() != null) {
        //字段值
      Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
      // issue #541 make property optional
      final String property = propertyMapping.getProperty();
      if (property == null) {
        continue;
      } else if (value == DEFERED) {
        foundValues = true;
        continue;
      }
      if (value != null) {
        foundValues = true;
      }
      //赋值给对象
      if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
        // gcode issue #377, call setter on nulls (value is not 'found')
        metaObject.setValue(property, value);
      }
    }
  }
  return foundValues;
}

三、handleResultSets流程

1. handleResultSets流程图

handleResultSets流程图

2. handleResultSet流程图

对于简单形式的ResultMap:
handleResultSet流程图​​​​​​​

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Mybatis是一个轻量级的Java持久层开源框架,它封装了JDBC操作数据库的底层细节,提供了一个简单易用的数据库访问方式。 Mybatis源码分为核心模块和附加模块两部分,核心模块主要包括配置解析、SQL解析、SQL执行等功能,附加模块包括连接池、缓存、事务管理等功能。 在Mybatis源码中,配置解析是其中的关键部分。通过解析mybatis-config.xml配置文件,可以获取到数据库连接信息、映射器配置、插件配置等。在配置解析过程中,Mybatis会对配置文件进行校验,确保配置的正确性。 SQL解析Mybatis的另一个重要功能。Mybatis通过解析Mapper接口中的注解或XML配置文件中的SQL语句,将SQL语句解析为ParameterMapping、BoundSql等对象,并将其封装成一个MappedStatement对象,供后续的SQL执行使用。 SQL执行是Mybatis的核心功能之一。在SQL执行阶段,Mybatis会根据MappedStatement中的信息,获取数据库连接,并执行对应的SQL语句。在执行过程中,Mybatis会通过TypeHandler对参数进行类型转换,并使用ResultSetHandler将查询结果封装成Java对象。 除了核心模块,Mybatis源码还包括了连接池、缓存、事务管理等附加模块的实现。连接池模块负责管理数据库连接的获取和释放,缓存模块负责缓存查询结果以提高性能,而事务管理模块则负责管理数据库的事务处理。 总之,Mybatis源码解析涉及多个关键模块的实现,包括配置解析、SQL解析、SQL执行、连接池、缓存、事务管理等。通过了解这些模块的实现原理,我们可以更好地理解和使用Mybatis框架。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值