mybatis源码学习------ResultSetHandler之嵌套映射

DefaultResultSetHandler的handleRowValues方法中有两个分支,一个用于处理嵌套映射,一个用于处理简单映射,本文讲分析其处理嵌套映射的分支。

handleRowValues方法的定义如下:

public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
  if (resultMap.hasNestedResultMaps()) {//是否存在嵌套结果映射
    //RowBounds配置校验
    ensureNoRowBounds();
    //ResultHandler校验
    checkResultHandler();
    handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
  } else {//简单映射
    handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
  }
}

ensureNoRowBounds

safeRowBoundsEnabled属性的含义为:是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。默认值为false

private void ensureNoRowBounds() {
  if (configuration.isSafeRowBoundsEnabled() && rowBounds != null && (rowBounds.getLimit() < RowBounds.NO_ROW_LIMIT || rowBounds.getOffset() > RowBounds.NO_ROW_OFFSET)) {
    throw new ExecutorException("Mapped Statements with nested result mappings cannot be safely constrained by RowBounds. "
        + "Use safeRowBoundsEnabled=false setting to bypass this check.");
  }
}

checkResultHandler

safeResultHandlerEnabled属性的含义为:是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。默认值为true

protected void checkResultHandler() {
  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.");
  }
}

handleRowValuesForNestedResultMap

处理嵌套映射的代码如下,主要用于解决嵌套嵌套嵌套的情况。

private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
  //创建上下文辅助类
  final DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
  //获取结果集
  ResultSet resultSet = rsw.getResultSet();
  //跳过指定记录数
  skipRows(resultSet, rowBounds);
  Object rowValue = previousRowValue;
  //判断能否继续映射结果集中剩余的记录
  while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
    //处理鉴别器,确定映射所使用的ResultMap对象
    final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
    //为当前记录创建对应的CacheKey对象
    final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);
    //根据上面创建的从嵌套映射关联的结果对象集合中查询
    Object partialObject = nestedResultObjects.get(rowKey);
    if (mappedStatement.isResultOrdered()) {//resultOrdered属性为真
      if (partialObject == null && rowValue != null) {//partialObject为null表示主结果发生变化
        //释放nestedResultObjects的空间
        nestedResultObjects.clear();
        //保存结果对象
        storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
      }
      //完成对该记录的映射
      rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
    } else {
      //完成对该记录的映射
      rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
      if (partialObject == null) {//partialObject为null表示主结果发生变化
        storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
      }
    }
  }
  if (rowValue != null && mappedStatement.isResultOrdered() && shouldProcessMoreRows(resultContext, rowBounds)) {
    storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
    previousRowValue = null;
  } else if (rowValue != null) {
    previousRowValue = rowValue;
  }
}

方法的流程图如下:
mybatis源码学习------ResultSetHandler之嵌套映射_第1张图片

大致了解其流程之后,下面开始详细分析其中的每一步是如何实现的。

skipRows&shouldProcessMoreRow&resolveDiscriminatedResultMap

这三个方法在上一篇分析简单映射中已经做了分析。

createRowKey

该方法用于为当前记录生成一个CacheKey对象,用于标识当前记录对应的结果对象,也可以作为缓存的key。

该方法的逻辑大致如下:

  1. 获取ResultMapping数组用于生成CacheKey对象,如果用户配置了,则会使用配置的id属性集合创建CacheKey对象。
  2. 如果用户没有配置,则使用用户显示配置的属性集合创建CacheKey对象
  3. 如果用户既没有配置也没有显示配置,则判断ResultMap的type是否为Map类型,如果是Map,则根据所有列名和列值创建CacheKey对象。如果不是Map,使用所有未映射的列名及列值创建CacheKey对象。

代码如下:

private CacheKey createRowKey(ResultMap resultMap, ResultSetWrapper rsw, String columnPrefix) throws SQLException {
  final CacheKey cacheKey = new CacheKey();
  cacheKey.update(resultMap.getId());
  List<ResultMapping> resultMappings = getResultMappingsForRowKey(resultMap);
  if (resultMappings.isEmpty()) {
    if (Map.class.isAssignableFrom(resultMap.getType())) {//resultMap的类型为Map
      //根据所有列名和列值创建CacheKey对象
      createRowKeyForMap(rsw, cacheKey);
    } else {
      //使用所有未映射的列名及列值创建CacheKey对象
      createRowKeyForUnmappedProperties(resultMap, rsw, cacheKey, columnPrefix);
    }
  } else {
    //使用resultMappings中的列名及列值创建CacheKey对象
    createRowKeyForMappedProperties(resultMap, rsw, cacheKey, resultMappings, columnPrefix);
  }
  if (cacheKey.getUpdateCount() < 2) {
    return CacheKey.NULL_CACHE_KEY;
  }
  return cacheKey;
}

getResultMappingsForRowKey

获取ResultMapping数组用于生成CacheKey对象。

  1. 如果用户配置了,则会使用配置的id属性集合。

  2. 如果用户没有配置,则会使用所有配置的属性集合

    private List<ResultMapping> getResultMappingsForRowKey(ResultMap resultMap) {
      List<ResultMapping> resultMappings = resultMap.getIdResultMappings();
      if (resultMappings.isEmpty()) {
        resultMappings = resultMap.getPropertyResultMappings();
      }
      return resultMappings;
    }
    

createRowKeyForMap

使用结果集中所有的列名和列值创建CacheKey对象

private void createRowKeyForMap(ResultSetWrapper rsw, CacheKey cacheKey) throws SQLException {
  List<String> columnNames = rsw.getColumnNames();
  for (String columnName : columnNames) {
    final String value = rsw.getResultSet().getString(columnName);
    if (value != null) {
      cacheKey.update(columnName);
      cacheKey.update(value);
    }
  }
}

createRowKeyForUnmappedProperties

使用所有未映射的列明及列值创建CacheKey对象

private void createRowKeyForUnmappedProperties(ResultMap resultMap, ResultSetWrapper rsw, CacheKey cacheKey, String columnPrefix) throws SQLException {
  final MetaClass metaType = MetaClass.forClass(resultMap.getType(), reflectorFactory);
  List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
  for (String column : unmappedColumnNames) {
    String property = column;
    //如果前缀不为空,就为列名拼接前缀
    if (columnPrefix != null && !columnPrefix.isEmpty()) {
      // When columnPrefix is specified, ignore columns without the prefix.
      if (column.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
        property = column.substring(columnPrefix.length());
      } else {
        continue;
      }
    }
    //metaType对象中存在property属性
    if (metaType.findProperty(property, configuration.isMapUnderscoreToCamelCase()) != null) {
      String value = rsw.getResultSet().getString(column);
      if (value != null) {
        cacheKey.update(column);
        cacheKey.update(value);
      }
    }
  }
}

createRowKeyForMappedProperties

逻辑与上面两个方法类似

private void createRowKeyForMappedProperties(ResultMap resultMap, ResultSetWrapper rsw, CacheKey cacheKey, List<ResultMapping> resultMappings, String columnPrefix) throws SQLException {
  for (ResultMapping resultMapping : resultMappings) {
    if (resultMapping.isSimple()) {
      final String column = prependPrefix(resultMapping.getColumn(), columnPrefix);
      final TypeHandler<?> th = resultMapping.getTypeHandler();
      List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
      // Issue #114
      if (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) {
        final Object value = th.getResult(rsw.getResultSet(), column);
        if (value != null || configuration.isReturnInstanceForEmptyRow()) {
          cacheKey.update(column);
          cacheKey.update(value);
        }
      }
    }
  }
}

getRowValue

通过上一篇简单映射可知getRowValue方法用于对记录值的映射,在处理嵌套映射的时候会调用getRowValue的另一个重载方法来完成对记录值的映射。

getRowValue方法如下:

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, String columnPrefix, Object partialObject) throws SQLException {
  final String resultMapId = resultMap.getId();
  Object rowValue = partialObject;//主记录对象
  if (rowValue != null) {//外层对象存在
    final MetaObject metaObject = configuration.newMetaObject(rowValue);
    putAncestor(rowValue, resultMapId);
    //处理嵌套映射
    applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
    ancestorObjects.remove(resultMapId);
  } else {//外层对象不存在
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    //创建外层对象
    rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
    if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
      final MetaObject metaObject = configuration.newMetaObject(rowValue);
      boolean foundValues = this.useConstructorMappings;
      if (shouldApplyAutomaticMappings(resultMap, true)) {//是否处理自动映射
        foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
      }
      //处理属性集合
      foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
      //维护当前结果对象和所属resultMap的关系
      putAncestor(rowValue, resultMapId);
      //处理嵌套映射
      foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
      //移除外层对象
      ancestorObjects.remove(resultMapId);
      foundValues = lazyLoader.size() > 0 || foundValues;
      rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
    }
    if (combinedKey != CacheKey.NULL_CACHE_KEY) {
      nestedResultObjects.put(combinedKey, rowValue);
    }
  }
  return rowValue;
}

applyNestedResultMappings

通过ancestorObjects集合来处理循环引用问题,如果发现存在循环引用则判断需要的对象是否在ancestorObjects集合中存在。如果存在则复用之前已经创建好的对象,如果不存在才会创建新对象。

private boolean applyNestedResultMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String parentPrefix, CacheKey parentRowKey, boolean newObject) {
  boolean foundValues = false;
  for (ResultMapping resultMapping : resultMap.getPropertyResultMappings()) {
    final String nestedResultMapId = resultMapping.getNestedResultMapId();
    if (nestedResultMapId != null && resultMapping.getResultSet() == null) {//存在嵌套映射的情况
      try {
        final String columnPrefix = getColumnPrefix(parentPrefix, resultMapping);
        //获取嵌套的ResultMap对象
        final ResultMap nestedResultMap = getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix);
        if (resultMapping.getColumnPrefix() == null) {
          //处理循环依赖的逻辑
          Object ancestorObject = ancestorObjects.get(nestedResultMapId);
          if (ancestorObject != null) {
            if (newObject) {
              linkObjects(metaObject, resultMapping, ancestorObject); // issue #385
            }
            continue;
          }
        }
        final CacheKey rowKey = createRowKey(nestedResultMap, rsw, columnPrefix);
        //合并两个cacheKey
        final CacheKey combinedKey = combineKeys(rowKey, parentRowKey);
        Object rowValue = nestedResultObjects.get(combinedKey);
        boolean knownValue = rowValue != null;
        //如果当前属性是集合类型的话,先对该属性进行初始化
        instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject); // mandatory
        //判断当前属性是否配置了必须非空,如果为真,则需要创建该属性对应的对象并复制
        if (anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw)) {
          //递归处理多层嵌套情况
          rowValue = getRowValue(rsw, nestedResultMap, combinedKey, columnPrefix, rowValue);
          if (rowValue != null && !knownValue) {
            //关联内外层对象
            linkObjects(metaObject, resultMapping, rowValue);
            foundValues = true;
          }
        }
      } catch (SQLException e) {
        throw new ExecutorException("Error getting nested result map values for '" + resultMapping.getProperty() + "'.  Cause: " + e, e);
      }
    }
  }
  return foundValues;
}
linkObjects

将当前对象设置到所属的外层对象的对应属性中

private void linkObjects(MetaObject metaObject, ResultMapping resultMapping, Object rowValue) {
  final Object collectionProperty = instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);
  if (collectionProperty != null) {
    final MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty);
    targetMetaObject.add(rowValue);
  } else {
    metaObject.setValue(resultMapping.getProperty(), rowValue);
  }
}
combineKeys

合并两个CacheKey对象

private CacheKey combineKeys(CacheKey rowKey, CacheKey parentRowKey) {
  if (rowKey.getUpdateCount() > 1 && parentRowKey.getUpdateCount() > 1) {
    CacheKey combinedKey;
    try {
      combinedKey = rowKey.clone();
    } catch (CloneNotSupportedException e) {
      throw new ExecutorException("Error cloning cache key.  Cause: " + e, e);
    }
    combinedKey.update(parentRowKey);
    return combinedKey;
  }
  return CacheKey.NULL_CACHE_KEY;
}
instantiateCollectionPropertyIfAppropriate

如果当前属性在助对象中是集合类型,则创建一个ArrayList实例,并赋给主对象,防止后面的操作出现空指针异常

private Object instantiateCollectionPropertyIfAppropriate(ResultMapping resultMapping, MetaObject metaObject) {
  final String propertyName = resultMapping.getProperty();
  Object propertyValue = metaObject.getValue(propertyName);
  if (propertyValue == null) {
    Class<?> type = resultMapping.getJavaType();
    if (type == null) {
      type = metaObject.getSetterType(propertyName);
    }
    try {
      if (objectFactory.isCollection(type)) {
        propertyValue = objectFactory.create(type);
        metaObject.setValue(propertyName, propertyValue);
        return propertyValue;
      }
    } catch (Exception e) {
      throw new ExecutorException("Error instantiating collection property for result '" + resultMapping.getProperty() + "'.  Cause: " + e, e);
    }
  } else if (objectFactory.isCollection(propertyValue.getClass())) {
    return propertyValue;
  }
  return null;
}
anyNotNullColumnHasValue

notNullColumn属性的作用:默认情况下,在至少一个被映射到属性的列不为空时,子对象才会被创建。 你可以在这个属性上指定非空的列来改变默认行为,指定后,Mybatis 将只在这些列非空时才创建一个子对象。可以使用逗号分隔来指定多个列。默认值:未设置(unset)。

在结果集中任何一个非空的列存在值则直接返回true。

private boolean anyNotNullColumnHasValue(ResultMapping resultMapping, String columnPrefix, ResultSetWrapper rsw) throws SQLException {
  Set<String> notNullColumns = resultMapping.getNotNullColumns();
  if (notNullColumns != null && !notNullColumns.isEmpty()) {
    ResultSet rs = rsw.getResultSet();
    for (String column : notNullColumns) {
      rs.getObject(prependPrefix(column, columnPrefix));
      if (!rs.wasNull()) {
        return true;
      }
    }
    return false;
  } else if (columnPrefix != null) {
    for (String columnName : rsw.getColumnNames()) {
      if (columnName.toUpperCase().startsWith(columnPrefix.toUpperCase())) {
        return true;
      }
    }
    return false;
  }
  return true;
}

你可能感兴趣的:(源码学习,mybatis,mybatis,mybatis源码,嵌套映射)