【Mybatis源码分析】09-ResultSetHandler

ResultSetHandler提供了处理不同Statement的方法,我们这里分析最常用的handleResultSets。调用此方法将一个Statement对象转换为一个List对象。

public List handleResultSets(Statement stmt) throws SQLException {
  ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
  final List multipleResults = new ArrayList();
  int resultSetCount = 0;
  ResultSetWrapper rsw = getFirstResultSet(stmt);
  List resultMaps = mappedStatement.getResultMaps();
  int resultMapCount = resultMaps.size();
  validateResultMapsCount(rsw, resultMapCount);
  while (rsw != null && resultMapCount > resultSetCount) {
    ResultMap resultMap = resultMaps.get(resultSetCount);
    handleResultSet(rsw, resultMap, multipleResults, null);
    rsw = getNextResultSet(stmt);
    cleanUpAfterHandlingResultSet();
    resultSetCount++;
  }
  String[] resultSets = mappedStatement.getResultSets();
  if (resultSets != null) {
    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);
} 
  

getFirstResultSet方法获取第一个结果集,将传统JDBC的ResultSet包装成一个包含结果列元信息的ResultSetWrapper对象。之所以称之为第一个结果集是因为Mybatis考虑到了一个Statement执行了多个sql语句的情况,后面用while循环处理每个结果集,multipleResults中每个元素就是一个结果集的List对象,这也就解释了为什么mappedStatement.getResultMaps()返回的是一个List

public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
  super();
  this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
  this.resultSet = rs;
  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));
  }
}
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List 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());
  }
} 
  

如果没有自定义resultHandler默认使用DefaultResultHandler只有一个方法handleResult,将当前数据解析成的对象保存起来,以便最后条用getResultList方法获取全部对象。

public void handleResult(ResultContextextends Object> context) {
  list.add(context.getResultObject());
}
public List getResultList() {
  return list;
} 
  

整个的数据记录到对象的映射解析由handleRowValues方法负责,分为两种情况1存在结果嵌套2不存在结果嵌套。结果嵌套就是下面这种情况。

private String processNestedResultMappings(XNode context, List resultMappings) throws Exception {
  if ("association".equals(context.getName())
      || "collection".equals(context.getName())
      || "case".equals(context.getName())) {
    if (context.getStringAttribute("select") == null) {
      ResultMap resultMap = resultMapElement(context, resultMappings);
      return resultMap.getId();
    }
  }
  return null;
}

我们这里先分析非嵌套结果情况。

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
    throws SQLException {
  DefaultResultContext resultContext = new DefaultResultContext();
  skipRows(rsw.getResultSet(), rowBounds);
  while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
    ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
    Object rowValue = getRowValue(rsw, discriminatedResultMap);
    storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
  }
} 
  

DefaultResultContext会保存当前解析的对象以及解析的数量,由这个对象将解析对象传递给ResultHandler。

skipRows根据传递的RowBounds对象跳过offset指定的记录条数,此方法的跳过形式为ResultSet的next形式,因此属于内存的分页形式,所以针对数据稍微有点多的情况我们最好不使用RowBounds分页。

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

resolveDiscriminatedResultMap方法是根据鉴别器属性找对真正的ResultMap,如果没有配置此属性那么resultMap不变。

getRowValue方法就是数据columnName与对象propertyName的映射匹配了。

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

createResultObject首先创建一个结果对象,然后对这个对象赋值。

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
  this.useConstructorMappings = false; // reset previous mapping result
  final List> constructorArgTypes = new ArrayList>();
  final List constructorArgs = new ArrayList();
  Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
  if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    final List 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;
} 
  

首先根据ResultMap的type利用反射创建一个对象,然后查看ResultMap的映射关系是否存在select属性且开启了懒加载开关,将原始对象resultObject替换由ProxyFactory创建的代理对象,此处就是利用动态代理创建懒加载对象的起点,以后我们再分析这个动态代理的懒加载对象,回过头继续分析getRowValue方法。后面就是对resultObject对象属性赋值的过程。

对属性赋值分为两部分:

  1. applyAutomaticMappings(rsw, resultMap, metaObject, null) 根据columnName和type属性名映射赋值。
  2. applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null)根据我们配置ResultMap的column和property映射赋值

对于后者如果映射存在nestedQueryId,会调用getNestedQueryMappingValue方法获取返回值。

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();
  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) {
    final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
    final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
    final Class targetType = propertyMapping.getJavaType();
    if (executor.isCached(nestedQuery, key)) {
      executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
      value = DEFERED;
    } else {
      final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
      if (propertyMapping.isLazy()) {
        lazyLoader.addLoader(property, metaResultObject, resultLoader);
        value = DEFERED;
      } else {
        value = resultLoader.loadResult();
      }
    }
  }
  return value;
}

如果当前命名空间存在该属性的缓存值,直接调用executor.deferLoad完成属性赋值操作。

public void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class targetType) {
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  DeferredLoad deferredLoad = new DeferredLoad(resultObject, property, key, localCache, configuration, targetType);
  if (deferredLoad.canLoad()) {
    deferredLoad.load();
  } else {
    deferredLoads.add(new DeferredLoad(resultObject, property, key, localCache, configuration, targetType));
  }
}

canLoad()内判断当前key是否有缓存并且该缓存不是占位符则调用deferredLoad.load()方法,取出缓存内容根据property的type赋值。否则会先加入deferredLoads中,等到metaResultObject的原对象全部赋值完毕循环deferredLoads调用load完成其缓存对象的赋值。

void load() {
  @SuppressWarnings( "unchecked" )
  // we suppose we get back a List
  List list = (List) localCache.getObject(key);
  Object value = resultExtractor.extractObjectFromList(list, targetType);
  resultObject.setValue(property, value);
} 
  
public Object extractObjectFromList(List list, Class targetType) {
  Object value = null;
  if (targetType != null && targetType.isAssignableFrom(list.getClass())) {
    value = list;
  } else if (targetType != null && objectFactory.isCollection(targetType)) {
    value = objectFactory.create(targetType);
    MetaObject metaObject = configuration.newMetaObject(value);
    metaObject.addAll(list);
  } else if (targetType != null && targetType.isArray()) {
    Class arrayComponentType = targetType.getComponentType();
    Object array = Array.newInstance(arrayComponentType, list.size());
    if (arrayComponentType.isPrimitive()) {
      for (int i = 0; i < list.size(); i++) {
        Array.set(array, i, list.get(i));
      }
      value = array;
    } else {
      value = list.toArray((Object[])array);
    }
  } else {
    if (list != null && list.size() > 1) {
      throw new ExecutorException("Statement returned more than one row, where no more than one was expected.");
    } else if (list != null && list.size() == 1) {
      value = list.get(0);
    }
  }
  return value;
} 
  

如果当前命名空间不存在该属性的缓存,分为两种情况:

ResultMap的该属性配置为懒加载:构造一个ResultLoader对象放入懒加载队列中。

eager模式:直接调用resultLoader的loadResult方法,使用当前session的executor的query方法取出结果

public Object loadResult() throws SQLException {
  List list = selectList();
  resultObject = resultExtractor.extractObjectFromList(list, targetType);
  return resultObject;
}

private <E> List<E> selectList() throws SQLException {
  Executor localExecutor = executor;
  if (Thread.currentThread().getId() != this.creatorThreadId || localExecutor.isClosed()) {
    localExecutor = newExecutor();
  }
  try {
    return localExecutor.<E> query(mappedStatement, parameterObject, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, cacheKey, boundSql);
  } finally {
    if (localExecutor != executor) {
      localExecutor.close(false);
    }
  }
} 
  

对于懒加载模式只是lazyLoader.addLoader(property, metaResultObject, resultLoader);后续又没有对lazyLoader做什么操作,是怎么完成懒加载的呢?

其实并不是后续没有操作,只是隐藏的比较隐蔽,还记得本文上面分析的创建对象的时候如果有懒加载映射会将该空对象替换为一个代理的对象吧。

resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
这个代理对象保存了lazyLoader的引用,所以说不是不用,是时候未到,下一篇具体分析下懒加载对象。

你可能感兴趣的:(Mybatis源码分析)