10.结果集映射

1 ResultSetHandler简介

  • 处理Statement执行后产生的结果集,生成结果列表
  • 处理存储过程执行后的输出参数
public interface ResultSetHandler {
    // 将Statement执行后产生的结果集(可能有多个结果集)映射为结果列表
    <E> List<E> handleResultSets(Statement var1) throws SQLException;

    <E> Cursor<E> handleCursorResultSets(Statement var1) throws SQLException;
	// 处理存储过程执行后的输出参数
    void handleOutputParameters(CallableStatement var1) throws SQLException;
}

ResultSetHandler的具体实现类是DefaultResultSetHandler,其实现的步骤就是将Statement执行后的结果集,按照Mapper文件中resultMap配置的ResultType或ResultMap来封装成对应的对象,最后将封装的对象返回 。

image-20210921235701196

但在解析结果值的时候还依赖一些其他的类。

image-20210921234755299

  • ResultSetWrapper:ResultSet包装器,丰富ResultSet方法,包含了ResultSet相关元数据

  • ResultContext:结果值上下文,存储值结果值、总数等。

  • ResultHandler:结果处理器。做处理完的类型转换的结果,进行后置处理。

1.1 ResultSetWrapper

public class ResultSetWrapper {

  /**
   * 数据集
   */
  private final ResultSet resultSet;
  private final TypeHandlerRegistry typeHandlerRegistry;
  /**
   * 字段名集合
   */
  private final List<String> columnNames = new ArrayList<>();
  /**
   * 字段对应的javaType类型名
   */
  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<>();
  /**
   * 针对当前ResultSet中,resultMap中映射的字段名
   * key: resultMapId
   * value:resultMap映射字段
   */
  private final Map<String, List<String>> mappedColumnNamesMap = new HashMap<>();
  /**
   * 针对当前ResultSet中,resultMap中未映射的字段名
   * key: resultMapId
   * value:resultMap未映射字段
   */
  private final Map<String, List<String>> unMappedColumnNamesMap = new HashMap<>();

  public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
    super();
    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.resultSet = rs;
    // 从ResultSet中获取元数据
    final ResultSetMetaData metaData = rs.getMetaData();
    // 总列数
    final int columnCount = metaData.getColumnCount();
    for (int i = 1; i <= columnCount; i++) {
      // columnLabel代表as的值,columnName代表原名
      // 默认取columnLabel的值
      columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));
      // 每个字段对应的jdbc类型
      jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
      // 每个字段对应的java类型
      classNames.add(metaData.getColumnClassName(i));
    }
  }
    
  private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
    List<String> mappedColumnNames = new ArrayList<>();
    List<String> unmappedColumnNames = new ArrayList<>();
    final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);
    // resultMap已经手动定义映射的字段    
    final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);
    for (String columnName : columnNames) {
      final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);
      if (mappedColumns.contains(upperColumnName)) {
        mappedColumnNames.add(upperColumnName);
      } else {
        unmappedColumnNames.add(columnName);
      }
    }
    mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);
    unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);
  }
  
  // resultMap中已经映射的字段名  
  public List<String> getMappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
    List<String> mappedColumnNames = mappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
    if (mappedColumnNames == null) {
      loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
      mappedColumnNames = mappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
    }
    return mappedColumnNames;
  }

  // resultMap中没有映射的字段名   
  public List<String> getUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
    List<String> unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
    if (unMappedColumnNames == null) {
      loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
      unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
    }
    return unMappedColumnNames;
  }  
}

1.2 ResultContext

public interface ResultContext<T> {

  T getResultObject();

  int getResultCount();

  boolean isStopped();

  void stop();

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

  public void nextResultObject(T resultObject) {
    resultCount++;
    this.resultObject = resultObject;
  }

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

1.3 ResultHandler

传入ResultContext,进行结果后置处理

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<?> context) {
    // 就仅当结果存储
    list.add(context.getResultObject());
  }

  public List<Object> getResultList() {
    return list;
  }
}

2 ResultMap结果集映射

映射是指返回的ResultSet列与Java Bean 属性之间的对应关系。通过ResultMapping进行映射描述,在用ResultMap封装成一个整体。

image-20210922000607528

2.1 映射设置

一个ResultMap 中包含多个ResultMapping 表示一个具体的JAVA属性到列的映射,其主要值如下:

image-20210922000635207

ResultMapping 有多种表现形式如下:

  1. constructor:构建参数字段
  2. id:ID字段
  3. result:普通结构集字段
  4. association:1对1关联字段
  5. Collection:1对多集合关联字段

image-20210922000717742

2.2 自动映射

当前列名和属性名相同的情况下,可使用自动映射

image-20210922000906125

自动映射条件

  1. 列名和属性名同时存在(勿略大小写,或者使用_拼接)
  2. 当前列未手动设置映射
  3. 属性类别存在TypeHandler
  4. 开启autoMapping (默认开启)

2.3 手动映射

mapper xml文件配置resultMap,其查询字段名称,存在resultMap中。blobWeb在resultMap中没有自定义手动映射,会采用自动映射赋值(开启autoMapping

<resultMap id="blogMap" type="com.scarecrow.mybatis.po.BlogPO">
  <id column="id" property="id"/>
  <result column="title" property="title"/>
</resultMap>

  <select id="getBlogById" resultMap="blogMap">
    select id, title, blobWeb
    from tb_blog
      where id = #{id,jdbcType=BIGINT}
  </select>

3 handleResultSets 简单结果集映射

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

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

  int resultSetCount = 0;
  // 包装ResultSet
  ResultSetWrapper rsw = getFirstResultSet(stmt);

  List<ResultMap> resultMaps = mappedStatement.getResultMaps();
  int resultMapCount = resultMaps.size();
  // 有结果集返回,没有ResultMap异常
  validateResultMapsCount(rsw, resultMapCount);
  while (rsw != null && resultMapCount > resultSetCount) {
    ResultMap resultMap = resultMaps.get(resultSetCount);
    // 处理当前结果集
    handleResultSet(rsw, resultMap, multipleResults, null);
    // 下一个结果集
    rsw = getNextResultSet(stmt);
    cleanUpAfterHandlingResultSet();
    resultSetCount++;
  }

  // 多结果集情况,mapper.xml select设置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++;
    }
  }

  return collapseSingleResultList(multipleResults);
}

3.1 handleResultSet

根据ResultMap与ResultSet处理当前结果集

/**
 * 构建出来的结果对象,如果父级结果属性映射不为null,会将结果对象赋值到父级结果属性对应的结果对象中,
 * 否则将结果对象加入到reusltHandler中。最后从reusltHandler中取的最终的结果对象加入到多个结果
 * 对象集合中
 *
 * @param rsw             结果集包装对象
 * @param resultMap       resultMap标签对象
 * @param multipleResults 多个结果对象集合
 * @param parentMapping   父级结果属性映射
 * @throws SQLException
 */
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
  try {
    // 如果父级结果属性映射不为null
    if (parentMapping != null) {
      handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
    } else {
      if (resultHandler == null) {
        // 设置默认的resultHandler
        DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
        // 构建出来的结果对象,如果父级结果属性映射不为null,会将结果对象赋值到父级结果属性对应的结果对象中,否则将结果对象加入到reusltHandler中
        handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
        // 结果集每一行映射的对象加入最终返回的List中
        multipleResults.add(defaultResultHandler.getResultList());
      } else {
        handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
      }
    }
  } finally {
    // issue #228 (close resultsets)
    closeResultSet(rsw.getResultSet());
  }
}

3.2 handleRowValues

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

3.3 handleRowValuesForSimpleResultMap

处理简单非嵌套结果集

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
    throws SQLException {
  // 新建一个默认的结果上下文
  DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
  ResultSet resultSet = rsw.getResultSet();
  // 查询结果集,跳过指定的行
  skipRows(resultSet, rowBounds);
  // 如果还有结果需要处理 而且 结果集还没有关闭 而且 结果集还有数据
  while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
    // 逐层解析resultMap 鉴别器Discriminator,取得最终的ResultMap标签对象
    ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
    //根据rsw 和resultMap 构建出对象
    Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
    storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
  }
}

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.4 getRowValue

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
  final ResultLoaderMap lazyLoader = new ResultLoaderMap();
  Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
  // 结果对象不为null 但没有对应结果类型的TypeHandler
  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, columnPrefix) || foundValues;
    }
    foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
    foundValues = lazyLoader.size() > 0 || foundValues;
    rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
  }
  return rowValue;
}

3.5 createResultObject返回结果集对象

主要处理返回结果的对象有以下几种情况

  • 返回结果类型有TypeHandler,会构造返回对象同时设置返回值

  • 返回结果类型与resultMap指定了constructor标签对应,resultMap会有constructorResultMappings。会根据constructor 找构造函数创建对象并设置构造函数参数的值

    <resultMap id="resultMap" type="com.scarecrow.mybatis.po.UserPO">
      <constructor>
        <arg column="USER_ID" javaType="long" jdbcType="DECIMAL"/>
        <arg column="USER_NAME" javaType="string" jdbcType="VARCHAR"/>
      </constructor>
      <result column="USER_ADDRESS" property="userAddress" jdbcType="VARCHAR"/>
    </resultMap>
    
  • 返回结果类型是接口或者是存在无参的默认的构造函数

  • 以上都不符合,开启了AutomaticMapping。根据构造函数来创建对象

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<>();
  final List<Object> constructorArgs = new ArrayList<>();
  // 取得构造函数所需的参数值去创建结果对象
  Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
  // 如果结果对象不为null但没有结果类型对应的TypeHandler
  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;
}

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();
  // 如果存在对应的TypeHandler
  if (hasTypeHandlerForResultObject(rsw, resultType)) {
    // 比如select只有一个字段count,resultMap的type为long。就会走这个分支。并且使用LongTypeHandler.getResult直接返回对象
    return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
  } else if (!constructorMappings.isEmpty()) {
    // 根据构造函数映射构建的结果对象,部分字段会有值
    return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
  } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
    // 如果resultType是接口或者resultType有默认的无参构造函数
    return objectFactory.create(resultType);
  } else if (shouldApplyAutomaticMappings(resultMap, false)) {
    // 是否可以应用自动映射。autoMapping:默认为null
    // 根据构造函数的 参数类型自动映射
    return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
  }
  throw new ExecutorException("Do not know how to create an instance of " + resultType);
}

3.5.1 返回结果类型有TypeHandler

这种情况一般只会返回一列

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);
  }
  final TypeHandler<?> typeHandler = rsw.getTypeHandler(resultType, columnName);
  // 获取结果值  
  return typeHandler.getResult(rsw.getResultSet(), columnName);
}

3.5.2 resultMap指定了constructor标签

Object createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType, List<ResultMapping> constructorMappings,
                                       List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) {
  boolean foundValues = false;
  // 遍历构造函数结果映射关系
  for (ResultMapping constructorMapping : constructorMappings) {
    // 获取构造函数参数映射关系对应java类型
    final Class<?> parameterType = constructorMapping.getJavaType();
    // 获取构造函数参数映射关系对应的列名
    final String column = constructorMapping.getColumn();
    final Object value;
    try {
      // 如果构造函数参数映射关系配置了嵌套查询的select标签ID
      if (constructorMapping.getNestedQueryId() != null) {
        value = getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix);
      }
      // 如果构造函数参数映射关系配置了嵌套的resultMapId
      else if (constructorMapping.getNestedResultMapId() != null) {
        final ResultMap resultMap = configuration.getResultMap(constructorMapping.getNestedResultMapId());
        value = getRowValue(rsw, resultMap, getColumnPrefix(columnPrefix, constructorMapping));
      }
      // 不存在嵌套的情况
      else {
        // 获取构造函数参数映射的TypeHandler
        final TypeHandler<?> typeHandler = constructorMapping.getTypeHandler();
        // 从ResultSet中取出columnName对应数据,然后转换为 java 对象
        value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(column, columnPrefix));
      }
    } catch (ResultMapException | SQLException e) {
      throw new ExecutorException("Could not process result for mapping: " + constructorMapping, e);
    }
    // 将构造函数参数类型添加到constructorArgTypes中
    constructorArgTypes.add(parameterType);
    // 将构造函数参数对象添加到construtorArgs中
    constructorArgs.add(value);
    // 只要有一个构造函数参数对象不为null,foundValue都会为true
    foundValues = value != null || foundValues;
  }
  // 利用反射根据构造参数类型获取 构造参数并创建对象 
  return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
}

3.5.3 无参构造函数

仅仅返回创建的对象

3.5.4 createByConstructorSignature

  • 只有一个构造函数
  • 多个构造函数使用了AutomapConstructor注解

会使用构造函数创建对象,并设置值(根据构造函数的参数)

private Object createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) throws SQLException {
  // 所有的构造函数
  final Constructor<?>[] constructors = resultType.getDeclaredConstructors();
  // 只有一个构造函数,或者多个构造函数中有AutomapConstructor 注解标识
  final Constructor<?> defaultConstructor = findDefaultConstructor(constructors);
  if (defaultConstructor != null) {
    return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, defaultConstructor);
  } else {
    for (Constructor<?> constructor : constructors) {
      // 构造函数的参数与返回列数一致
      // 构造函数参数的数据类型与返回列的jdbcType匹配到 相应的TypeHandler
      if (allowedConstructorUsingTypeHandlers(constructor, rsw.getJdbcTypes())) {
        // 根据构造参数的个数与类型 创建返回对象
        return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, constructor);
      }
    }
  }
  throw new ExecutorException("No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames());
}
private Object createUsingConstructor(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, 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 = rsw.getTypeHandler(parameterType, columnName);
    Object value = typeHandler.getResult(rsw.getResultSet(), columnName);
    constructorArgTypes.add(parameterType);
    constructorArgs.add(value);
    foundValues = value != null || foundValues;
  }
  return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
}

3.6 行结果集映射

getRowValue 主要包含两部分:

  • 创建返回对象
  • 为返回对象赋值
    • 自动映射(查询的列没有 在resultMap指定)
    • 手动映射(resultMap指定了查询的列)

3.6.1 自动映射

private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
  // 创建自动映射 AutomaticMappings
  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 List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
  final String mapKey = resultMap.getId() + ":" + columnPrefix;
  List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
  if (autoMapping == null) {
    autoMapping = new ArrayList<>();
    // 未映射的列  
    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;
        }
      }
      // 通过未映射的列,去获取javaBean相应的属性  
      final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
      if (property != null && metaObject.hasSetter(property)) {
        if (resultMap.getMappedProperties().contains(property)) {
          continue;
        }
        // 通过未映射的列,去获取javaBean相应的属性类型  
        final Class<?> propertyType = metaObject.getSetterType(property);
        if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
          final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
          // 获取相应的 typeHandler 
          autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
        } else {
          configuration.getAutoMappingUnknownColumnBehavior()
              .doAction(mappedStatement, columnName, property, propertyType);
        }
      } else {
        configuration.getAutoMappingUnknownColumnBehavior()
            .doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
      }
    }
    autoMappingsCache.put(mapKey, autoMapping);
  }
  return autoMapping;
}

3.6.2 手动映射

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 = 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 == DEFERRED) {
        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;
}

4. 嵌套查询

  • 关联的嵌套 Select 查询
    <resultMap id="blogResultNestedQuery" type="com.scarecrow.mybatis.po.BlogPO">
      <id column="id" property="id"/>
      <result column="title" property="title"/>
      <result column="blobWeb" property="blobWeb"/>
      <association property="authorPO" javaType="com.scarecrow.mybatis.po.AuthorPO" column="id" select="selectAuthorNestedQuery"/>
    </resultMap>
    
    <resultMap id="authorResultNestedQuery" type="com.scarecrow.mybatis.po.AuthorPO">
      <id column="id" property="id"/>
      <result column="username" property="username"/>
      <result column="email" property="email"/>
    </resultMap>
    
    <select id="selectBlogNestedQuery" resultMap="blogResultNestedQuery">
      select id, title, blobWeb
      from tb_blog
      where id = #{id,jdbcType=BIGINT}
    </select>
    
    <select id="selectAuthorNestedQuery" resultMap="authorResultNestedQuery">
      select id, username, email
      from tb_author
      where id = #{id,jdbcType=BIGINT}
    </select>
    
  • 关联的嵌套结果映射
    • 外部的嵌套结果映射

      <resultMap id="blogAuthorNestedExternalMap" type="com.scarecrow.mybatis.po.BlogPO">
        <id column="id" property="id"/>
        <result column="title" property="title"/>
        <result column="blobWeb" property="blobWeb"/>
        <association property="authorPO" javaType="com.scarecrow.mybatis.po.AuthorPO" resultMap="authorNestedExternalMap"/>
      </resultMap>
      
      <resultMap id="authorNestedExternalMap" type="com.scarecrow.mybatis.po.AuthorPO">
        <id property="id" column="id" jdbcType="BIGINT"/>
        <result property="username" column="username" jdbcType="VARCHAR"/>
        <result property="email" column="email" jdbcType="VARCHAR"/>
      </resultMap>
      
      <select id="getBlogAndAuthorExternal" resultMap="blogAuthorNestedExternalMap">
        select b.id, b.title, b.blobWeb, a.id, a.username, a.email
        from tb_blog b, tb_author a
        where b.id = a.blog_id
        and b.id = #{id,jdbcType=BIGINT}
      </select>
      
    • 内部嵌套结果映射

      <!--嵌套结果映射-->
      <resultMap id="blogAuthorNestedResultsMap" type="com.scarecrow.mybatis.po.BlogPO">
        <id column="id" property="id"/>
        <result column="title" property="title"/>
        <result column="blobWeb" property="blobWeb"/>
        <association property="authorPO" javaType="com.scarecrow.mybatis.po.AuthorPO">
          <id property="id" column="id" jdbcType="BIGINT"/>
          <result property="username" column="username" jdbcType="VARCHAR"/>
          <result property="email" column="email" jdbcType="VARCHAR"/>
        </association>
      </resultMap>
      
      <select id="getBlogAndAuthorNestedResultsById" resultMap="blogAuthorNestedResultsMap">
        select b.id, b.title, b.blobWeb, a.id, a.username, a.email
        from tb_blog b, tb_author a
        where b.id = a.blog_id
        and b.id = #{id,jdbcType=BIGINT}
      </select>
      

4.1 嵌套结果映射

4.1.1 嵌套结果映射xml解析

解析xml文件中的resultMap时会解析嵌套查询与嵌套结果集映射

image-20210928222314399

// 嵌套结果映射
private String processNestedResultMappings(XNode context, List<ResultMapping> resultMappings, Class<?> enclosingType) {
  if (Arrays.asList("association", "collection", "case").contains(context.getName())
      && context.getStringAttribute("select") == null) {
    validateCollection(context, enclosingType);
    // 解析内部的嵌套的ResultMap
    ResultMap resultMap = resultMapElement(context, resultMappings, enclosingType);
    return resultMap.getId();
  }
  return null;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DFBx9eiX-1635085902946)(…/…/AppData/Roaming/Typora/typora-user-images/image-20210928222841111.png)]

4.1.2 嵌套结果集映射

4.2关联的嵌套 Select 查询

from tb_blog b, tb_author a
where b.id = a.blog_id
and b.id = #{id,jdbcType=BIGINT}

```

4.1 嵌套结果映射

4.1.1 嵌套结果映射xml解析

解析xml文件中的resultMap时会解析嵌套查询与嵌套结果集映射

[外链图片转存中…(img-QxoQCsjR-1635085902945)]

// 嵌套结果映射
private String processNestedResultMappings(XNode context, List<ResultMapping> resultMappings, Class<?> enclosingType) {
  if (Arrays.asList("association", "collection", "case").contains(context.getName())
      && context.getStringAttribute("select") == null) {
    validateCollection(context, enclosingType);
    // 解析内部的嵌套的ResultMap
    ResultMap resultMap = resultMapElement(context, resultMappings, enclosingType);
    return resultMap.getId();
  }
  return null;
}

[外链图片转存中…(img-DFBx9eiX-1635085902946)]

4.1.2 嵌套结果集映射

4.2关联的嵌套 Select 查询

4.2.1 嵌套 Select 查询xml解析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值