花里胡哨系列之Mybatis源码---结果映射(七)

一.结果映射介绍

    在映射文件中,Mybatis可以根据配置,自动将查询结果封装成对象。常见的结果配置有:

    1.通过 resultType 映射

    

<select id="selectFromAuthor" parameterType="java.lang.String" resultType="org.apache.ibatis.domain.blog.Author">
   select * from Author where id = #{id} and username = #{username}
</select>

    2.通过 resultMap 映射

  

<resultMap id="selectAuthor" type="org.apache.ibatis.domain.blog.Author">
 <id column="id" property="id" />
 <result property="username" column="username" />
 <result property="password" column="password" />
 <result property="email" column="email" />
 <result property="bio" column="bio" />
 <result property="favouriteSection" column="favourite_section" />
</resultMap>
<select id="selectAuthor" parameterMap="selectAuthor" resultMap="selectAuthor">
 select * from Author where id = #{id} and username = #{username}
</select>

二.结果映射标签解析过程

     在映射文件加载过程中,上述两种方式结果映射会被解析成什么样呢?简单回顾一下

     首先回顾一下resultMap标签解析过程

XMLMapperBuilder:
private void configurationElement(XNode context) {
  try {
    //获取命名空间
    String namespace = context.getStringAttribute("namespace");
    if (namespace == null || namespace.equals("")) {
      throw new BuilderException("Mapper's namespace cannot be empty");
    }
    builderAssistant.setCurrentNamespace(namespace);
    // 解析 <cache-ref> 节点
    //在 MyBatis 中,二级缓存是可以共用的。这需要通过<cache-ref>节点为命名空间配置参
    //照缓存,引用其他命名空间的缓存,在一个mapper.xml文件中,cache 和 cache-ref一般只会存在一个
    //如果都存在,看具体解析的顺序
    cacheRefElement(context.evalNode("cache-ref"));
    //解析<cache> 节点,二级缓存,在解析 select|insert|update|delete 会将二级缓存设置进去
    cacheElement(context.evalNode("cache"));
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    sqlElement(context.evalNodes("/mapper/sql"));
    buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
  }
}
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings, Class<?> enclosingType) throws Exception {
  ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
  //解析type属性
  String type = resultMapNode.getStringAttribute("type",
      resultMapNode.getStringAttribute("ofType",
          resultMapNode.getStringAttribute("resultType",
              resultMapNode.getStringAttribute("javaType"))));
  Class<?> typeClass = resolveClass(type);
  if (typeClass == null) {
    typeClass = inheritEnclosingType(resultMapNode, enclosingType);
  }
  Discriminator discriminator = null;
  List<ResultMapping> resultMappings = new ArrayList<>();
  resultMappings.addAll(additionalResultMappings);
  List<XNode> resultChildren = resultMapNode.getChildren();
  for (XNode resultChild : resultChildren) {//解析子标签<result> </result>,<id>
    if ("constructor".equals(resultChild.getName())) {
      processConstructorElement(resultChild, typeClass, resultMappings);
    } else if ("discriminator".equals(resultChild.getName())) {
      //discriminator 根据数据库返回结果值,决定引用 哪个resultMap
      discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
    } else {
      List<ResultFlag> flags = new ArrayList<>();
      if ("id".equals(resultChild.getName())) {
        flags.add(ResultFlag.ID);
      }
      //解析id和type,  创建ResultMapping,获取<result> </result>标签中的所有属性
      resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
    }
  }
  //解析id属性
  String id = resultMapNode.getStringAttribute("id",
          resultMapNode.getValueBasedIdentifier());
  String extend = resultMapNode.getStringAttribute("extends");
  Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
  ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
  try {
    // 根据前面获取到的信息构建 ResultMap 对象, id --> ResultMap
    return resultMapResolver.resolve();
  } catch (IncompleteElementException  e) {
    configuration.addIncompleteResultMap(resultMapResolver);
    throw e;
  }
}
ResultMapResolver:
public ResultMap resolve() {
  return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings, this.autoMapping);
}
MapperBuilderAssistant:
public ResultMap addResultMap(
    String id,
    Class<?> type,
    String extend,
    Discriminator discriminator,
    List<ResultMapping> resultMappings,
    Boolean autoMapping) {
  id = applyCurrentNamespace(id, false);
  extend = applyCurrentNamespace(extend, true);

  if (extend != null) {
    if (!configuration.hasResultMap(extend)) {//抛异常,外部捕获,后续补偿
      throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
    }
    ResultMap resultMap = configuration.getResultMap(extend);
    List<ResultMapping> extendedResultMappings = new ArrayList<>(resultMap.getResultMappings());
    extendedResultMappings.removeAll(resultMappings);
    // Remove parent constructor if this resultMap declares a constructor.
    boolean declaresConstructor = false;
    for (ResultMapping resultMapping : resultMappings) {
      if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
        declaresConstructor = true;
        break;
      }
    }
    if (declaresConstructor) {
      extendedResultMappings.removeIf(resultMapping -> resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR));
    }
    resultMappings.addAll(extendedResultMappings);
  }
  ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping)
      .discriminator(discriminator)
      .build();
  configuration.addResultMap(resultMap);
  return resultMap;
}

        最终,ResultMap会被解析成ResultMap对象,简单的看一下这个对象的一下属性:

private Configuration configuration;
/**
 * <resultMap id="selectAuthor" type="org.apache.ibatis.domain.blog.Author">
 */
private String id;
private Class<?> type;
/**
 * <result></result>
 * <result></result>
 * <result></result>
 */
private List<ResultMapping> resultMappings;
private List<ResultMapping> idResultMappings;
private List<ResultMapping> constructorResultMappings;
private List<ResultMapping> propertyResultMappings;

/**
 * column 集合
 */
private Set<String> mappedColumns;
/**
 *  property 集合
 */
private Set<String> mappedProperties;
private Discriminator discriminator;
private boolean hasNestedResultMaps;
private boolean hasNestedQueries;
private Boolean autoMapping;

      接下来看看  resultType 这种方式,因为这个属性属于 <select>标签中的,所有解析过程在 解析<select>标签过程中

XMLMapperBuilder:
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
  for (XNode context : list) {
    final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
    try {
      //开始解析
      statementParser.parseStatementNode();
    } catch (IncompleteElementException e) {
      configuration.addIncompleteStatement(statementParser);
    }
  }
}
XMLStatementBuilder:
public void parseStatementNode() {
  String id = context.getStringAttribute("id");
  String databaseId = context.getStringAttribute("databaseId");

  if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
    return;
  }

  String nodeName = context.getNode().getNodeName();
  SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
  boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
  boolean useCache = context.getBooleanAttribute("useCache", isSelect);
  boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

  // Include Fragments before parsing,解析include标签
  XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
  includeParser.applyIncludes(context.getNode());

  String parameterType = context.getStringAttribute("parameterType");
  Class<?> parameterTypeClass = resolveClass(parameterType);

  String lang = context.getStringAttribute("lang");
  LanguageDriver langDriver = getLanguageDriver(lang);

  // Parse selectKey after includes and remove them.
  //解析<selectKey>标签
  // <selectKey keyProperty="id" resultType="int" order="BEFORE">
  //      select author_seq.nextval from dual
  //  </selectKey>
  processSelectKeyNodes(id, parameterTypeClass, langDriver);

  // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
  KeyGenerator keyGenerator;
  String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
  if (configuration.hasKeyGenerator(keyStatementId)) {
    keyGenerator = configuration.getKeyGenerator(keyStatementId);
  } else {
    keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
        configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
        ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
  }
  //解析sql语句
  SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
  StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
  Integer fetchSize = context.getIntAttribute("fetchSize");
  Integer timeout = context.getIntAttribute("timeout");
  String parameterMap = context.getStringAttribute("parameterMap");
  // resultType属性,最终解析成 ResultMap 对象
  String resultType = context.getStringAttribute("resultType");
  //通过别名解析 resultType 对应的类型
  Class<?> resultTypeClass = resolveClass(resultType);
  String resultMap = context.getStringAttribute("resultMap");
  String resultSetType = context.getStringAttribute("resultSetType");
  ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
  if (resultSetTypeEnum == null) {
    resultSetTypeEnum = configuration.getDefaultResultSetType();
  }
  String keyProperty = context.getStringAttribute("keyProperty");
  String keyColumn = context.getStringAttribute("keyColumn");
  String resultSets = context.getStringAttribute("resultSets");
  //创建 MappedStatement,包含了sql的所有信息,如sql语句,入参,结果映射,缓存, 保存到configuration中,
  builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
      fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
      resultSetTypeEnum, flushCache, useCache, resultOrdered,
      keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
MapperBuilderAssistant:
public MappedStatement addMappedStatement(
    String id,
    SqlSource sqlSource,
    StatementType statementType,
    SqlCommandType sqlCommandType,
    Integer fetchSize,
    Integer timeout,
    String parameterMap,
    Class<?> parameterType,
    String resultMap,
    Class<?> resultType,
    ResultSetType resultSetType,
    boolean flushCache,
    boolean useCache,
    boolean resultOrdered,
    KeyGenerator keyGenerator,
    String keyProperty,
    String keyColumn,
    String databaseId,
    LanguageDriver lang,
    String resultSets) {

  if (unresolvedCacheRef) {//抛异常,后续会继续创建MappedStatement
    throw new IncompleteElementException("Cache-ref not yet resolved");
  }

  id = applyCurrentNamespace(id, false);
  boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

  MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
      .resource(resource)
      .fetchSize(fetchSize)
      .timeout(timeout)
      .statementType(statementType)
      .keyGenerator(keyGenerator)
      .keyProperty(keyProperty)
      .keyColumn(keyColumn)
      .databaseId(databaseId)
      .lang(lang)
      .resultOrdered(resultOrdered)
      .resultSets(resultSets)
      .resultMaps(getStatementResultMaps(resultMap, resultType, id))//处理结果映射
      .resultSetType(resultSetType)
      .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
      .useCache(valueOrDefault(useCache, isSelect))
      .cache(currentCache);//设置二级缓存

  ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
  if (statementParameterMap != null) {
    statementBuilder.parameterMap(statementParameterMap);
  }

  MappedStatement statement = statementBuilder.build();
  configuration.addMappedStatement(statement);
  return statement;
}
private List<ResultMap> getStatementResultMaps(
    String resultMap,
    Class<?> resultType,
    String statementId) {
  resultMap = applyCurrentNamespace(resultMap, true);

  List<ResultMap> resultMaps = new ArrayList<>();
  if (resultMap != null) {//处理<resultMap>
    String[] resultMapNames = resultMap.split(",");
    for (String resultMapName : resultMapNames) {
      try {
        resultMaps.add(configuration.getResultMap(resultMapName.trim()));
      } catch (IllegalArgumentException e) {
        throw new IncompleteElementException("Could not find result map '" + resultMapName + "' referenced from '" + statementId + "'", e);
      }
    }
  } else if (resultType != null) {//处理 resultType 属性
    ResultMap inlineResultMap = new ResultMap.Builder(
        configuration,
        statementId + "-Inline",
        resultType,
        new ArrayList<>(),
        null).build();
    resultMaps.add(inlineResultMap);
  }
  return resultMaps;
}

     可以看到,resultType最终也会被封装成 ResultMap对象,这样方便了之后对结果集的处理。

三.处理结果集

     在执行完sql后,Mybatis根据配置将结果映射到相应的对象中。

    基本思路:

    1.封装结果集中的信息,如收集列名集合,列个数等

    2.获取对应的ResultMap对象,根据配置获取结果集中的结果,并设置到最终对象中

PreparedStatementHandler:
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  //执行sql
  ps.execute();
  //处理结果
  return resultSetHandler.handleResultSets(ps);
}
DefaultResultSetHandler:
@Override
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;
  //获取第一个结果集
  ResultSetWrapper rsw = getFirstResultSet(stmt);
  //获取配置的 <ResultMap>结果映射标签
  List<ResultMap> 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);
}
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
  //获取结果集
  ResultSet rs = stmt.getResultSet();
  while (rs == null) {
    /*
     * 移动 ResultSet 指针到下一个上,有些数据库驱动可能需要使用者
     * 先调用 getMoreResults 方法,然后才能调用 getResultSet 方法
     * 获取到第一个 ResultSet
     */
    // 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;
      }
    }
  }
  //对结果集信息进行包装,如列名集合
  return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}
ResultSetWrapper:
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));
  }
}

       结果集有了,ResultMap也有了,接下来就是根据ResultMap中的信息 在结果集中查找结果,关注handleResultSet(rsw, resultMap, multipleResults, null)

DefaultResultSetHandler:
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());
  }
}
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
  if (resultMap.hasNestedResultMaps()) {
    //处理嵌套映射,嵌套查询是指<ResultMap>中嵌套了一个<ResultMap>
    ensureNoRowBounds();
    checkResultHandler();
    handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
  } else {
    //处理简单映射
    handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
  }
}
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
    throws SQLException {
  DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
  ResultSet resultSet = rsw.getResultSet();
  // 根据 RowBounds 定位到指定行记录,可以根据RowBounds进行逻辑分页(一般不会这样做)
  skipRows(resultSet, rowBounds);
  // 检测是否还有更多行的数据需要处理,可以根据RowBounds进行逻辑分页(一般不会这样做)
  while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
    // 获取经过鉴别器处理后的 ResultMap
    ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
    //rowValue 最终封装的对象结果
    Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
    //保存结果
    storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
  }
}

简单看一下  skipRows(resultSet, rowBounds)

private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
  // 检测 rs 的类型,不同的类型行数据定位方式是不同的
  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;
      }
    }
  }
}
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
  final ResultLoaderMap lazyLoader = new ResultLoaderMap();
  //创建实体类对象
  Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
  if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    //对于简单类型,如int等有 typeHander处理器,则不会进入此处,rowValue已是最终结果
    final MetaObject metaObject = configuration.newMetaObject(rowValue);
    boolean foundValues = this.useConstructorMappings;
    // 检测是否应该自动映射结果集
    if (shouldApplyAutomaticMappings(resultMap, false)) {
      //自动映射,未在<resultMap>中配置的属性,如hashMap,自动映射返回对象(只配置resultType)
      foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
    }
    // 根据 <resultMap> 节点中配置的映射关系进行映射
    foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
    foundValues = lazyLoader.size() > 0 || foundValues;
    rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
  }
  return rowValue;
}

        getRowValue中,先创建返回对象,然后再进行结果映射。对应一下简单的返回类型,如 String,Integer等已经注册过TypeHandler,在创建返回对象时值已经设置到对象中,无需进行结果映射。

DefaultResultSetHandler:
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);
  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);
  // 获取 <constructor> 节点对应的 ResultMapping
  final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();

  // 检测是否有与返回值类型相对应的 TypeHandler,若有则直接从
  // 通过 TypeHandler 从结果集中提取数据,并生成返回值对象(后续无需再填充值)
  if (hasTypeHandlerForResultObject(rsw, resultType)) {
    // 通过 TypeHandler 获取取,并生成返回值对象
    return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
  } else if (!constructorMappings.isEmpty()) {
    // 通过 <constructor> 节点配置的映射信息从 ResultSet 中ᨀ取数据,
    // 然后将这些数据传给指定构造方法,即可创建实体类对象
    return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
  } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
    // 通过 ObjectFactory 调用目标类的默认构造方法创建实例
    return objectFactory.create(resultType);
  } else if (shouldApplyAutomaticMappings(resultMap, false)) {//检查是否自动映射
    // 通过自动映射查找合适的构造方法创建实例
    return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
  }
  throw new ExecutorException("Do not know how to create an instance of " + resultType);
}

       Mybatis默认注册了很多TypeHandler,TypeHandler 接口用来设置入参,获取结果集

public interface TypeHandler<T> {

  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  /**
   * @param columnName Colunm name, when configuration <code>useColumnLabel</code> is <code>false</code>
   */
  T getResult(ResultSet rs, String columnName) throws SQLException;

  T getResult(ResultSet rs, int columnIndex) throws SQLException;

  T getResult(CallableStatement cs, int columnIndex) throws SQLException;

}
public TypeHandlerRegistry() {
  register(Boolean.class, new BooleanTypeHandler());
  register(boolean.class, new BooleanTypeHandler());
  register(JdbcType.BOOLEAN, new BooleanTypeHandler());
  register(JdbcType.BIT, new BooleanTypeHandler());

  register(Byte.class, new ByteTypeHandler());
  register(byte.class, new ByteTypeHandler());
  register(JdbcType.TINYINT, new ByteTypeHandler());

  register(Short.class, new ShortTypeHandler());
  register(short.class, new ShortTypeHandler());
  register(JdbcType.SMALLINT, new ShortTypeHandler());

  register(Integer.class, new IntegerTypeHandler());
  register(int.class, new IntegerTypeHandler());
  register(JdbcType.INTEGER, new IntegerTypeHandler());

  register(Long.class, new LongTypeHandler());
  register(long.class, new LongTypeHandler());

  register(Float.class, new FloatTypeHandler());
  register(float.class, new FloatTypeHandler());
  register(JdbcType.FLOAT, new FloatTypeHandler());

  register(Double.class, new DoubleTypeHandler());
  register(double.class, new DoubleTypeHandler());
  register(JdbcType.DOUBLE, new DoubleTypeHandler());

  register(Reader.class, new ClobReaderTypeHandler());
  register(String.class, new StringTypeHandler());
  register(String.class, JdbcType.CHAR, new StringTypeHandler());
  register(String.class, JdbcType.CLOB, new ClobTypeHandler());
  register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
  register(String.class, JdbcType.LONGVARCHAR, new StringTypeHandler());
  register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
  register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
  register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
  register(JdbcType.CHAR, new StringTypeHandler());
  register(JdbcType.VARCHAR, new StringTypeHandler());
  register(JdbcType.CLOB, new ClobTypeHandler());
  register(JdbcType.LONGVARCHAR, new StringTypeHandler());
  register(JdbcType.NVARCHAR, new NStringTypeHandler());
  register(JdbcType.NCHAR, new NStringTypeHandler());
  register(JdbcType.NCLOB, new NClobTypeHandler());

  register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
  register(JdbcType.ARRAY, new ArrayTypeHandler());

  register(BigInteger.class, new BigIntegerTypeHandler());
  register(JdbcType.BIGINT, new LongTypeHandler());

  register(BigDecimal.class, new BigDecimalTypeHandler());
  register(JdbcType.REAL, new BigDecimalTypeHandler());
  register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
  register(JdbcType.NUMERIC, new BigDecimalTypeHandler());

  register(InputStream.class, new BlobInputStreamTypeHandler());
  register(Byte[].class, new ByteObjectArrayTypeHandler());
  register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
  register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
  register(byte[].class, new ByteArrayTypeHandler());
  register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
  register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
  register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
  register(JdbcType.BLOB, new BlobTypeHandler());

  register(Object.class, unknownTypeHandler);
  register(Object.class, JdbcType.OTHER, unknownTypeHandler);
  register(JdbcType.OTHER, unknownTypeHandler);

  register(Date.class, new DateTypeHandler());
  register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
  register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
  register(JdbcType.TIMESTAMP, new DateTypeHandler());
  register(JdbcType.DATE, new DateOnlyTypeHandler());
  register(JdbcType.TIME, new TimeOnlyTypeHandler());

  register(java.sql.Date.class, new SqlDateTypeHandler());
  register(java.sql.Time.class, new SqlTimeTypeHandler());
  register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());

  register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());

  register(Instant.class, new InstantTypeHandler());
  register(LocalDateTime.class, new LocalDateTimeTypeHandler());
  register(LocalDate.class, new LocalDateTypeHandler());
  register(LocalTime.class, new LocalTimeTypeHandler());
  register(OffsetDateTime.class, new OffsetDateTimeTypeHandler());
  register(OffsetTime.class, new OffsetTimeTypeHandler());
  register(ZonedDateTime.class, new ZonedDateTimeTypeHandler());
  register(Month.class, new MonthTypeHandler());
  register(Year.class, new YearTypeHandler());
  register(YearMonth.class, new YearMonthTypeHandler());
  register(JapaneseDate.class, new JapaneseDateTypeHandler());

  // issue #273
  register(Character.class, new CharacterTypeHandler());
  register(char.class, new CharacterTypeHandler());
}

       可以看到大部分常见的类型,都默认注册进去了,接下来看看 TypeHandler如何填充结果集

DefaultResultSetHandler:
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);
  }
  //获取 resultType 对应的 类型处理器
  final TypeHandler<?> typeHandler = rsw.getTypeHandler(resultType, columnName);
  //获取 columnName 对应的 值
  return typeHandler.getResult(rsw.getResultSet(), columnName);
}
BaseTypeHandler:
public T getResult(ResultSet rs, String columnName) throws SQLException {
  try {
     //模板方法,子类具体实现,以String为例
    return getNullableResult(rs, columnName);
  } catch (Exception e) {
    throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);
  }
}
StringTypeHandler:
public String getNullableResult(ResultSet rs, String columnName)
    throws SQLException {
 //获取结果集中的结果
  return rs.getString(columnName);
}

       对于没有注册TypeHandler 的返回类型,如自定义bean对象,则需要通过反射创建对象 objectFactory.create(resultType);

ExampleObjectFactory:
@Override
public <T> T create(Class<T> type) {
  return super.<T> create(type);
}
DefaultObjectFactory:
public <T> T create(Class<T> type) {
  return create(type, null, null);
}
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
  Class<?> classToCreate = resolveInterface(type);
  // we know types are assignable
  return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
}
private  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
  try {
    Constructor<T> constructor;
    if (constructorArgTypes == null || constructorArgs == null) {
      //默认的无参构造函数
      constructor = type.getDeclaredConstructor();
      try {
        //反射创建实例
        return constructor.newInstance();
      } catch (IllegalAccessException e) {
        if (Reflector.canControlMemberAccessible()) {
          constructor.setAccessible(true);
          return constructor.newInstance();
        } else {
          throw e;
        }
      }
    }
    //有参构造函数
    constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
    try {
      return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
    } catch (IllegalAccessException e) {
      if (Reflector.canControlMemberAccessible()) {
        constructor.setAccessible(true);
        return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
      } else {
        throw e;
      }
    }
  } catch (Exception e) {
    String argTypes = Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList)
        .stream().map(Class::getSimpleName).collect(Collectors.joining(","));
    String argValues = Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList)
        .stream().map(String::valueOf).collect(Collectors.joining(","));
    throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
  }
}

       创建好了返回实体对象,可以根据结果集和ResultMap进行映射了

DefaultResultSetHandler:
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
  final ResultLoaderMap lazyLoader = new ResultLoaderMap();
  //创建实体类对象
  Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
  if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    //对于简单类型,如int等有 typeHander处理器,则不会进入此处,rowValue已是最终结果
    final MetaObject metaObject = configuration.newMetaObject(rowValue);
    boolean foundValues = this.useConstructorMappings;
    // 检测是否应该自动映射结果集
    if (shouldApplyAutomaticMappings(resultMap, false)) {
      //自动映射,未在<resultMap>中配置的属性,如hashMap,自动映射返回对象(只配置resultType)
      foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
    }
    // 根据 <resultMap> 节点中配置的映射关系进行映射
    foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
    foundValues = lazyLoader.size() > 0 || foundValues;
    rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
  }
  return rowValue;
}

        什么是自动映射,例如:

<select id="selectFromAuthor" parameterType="java.lang.String" resultType="org.apache.ibatis.domain.blog.Author">
   select * from Author where id = #{id} and username = #{username}
</select>

   返回类型并没有使用 resultMap,且 resultType的类型 并没有特殊的TypeHandler处理,这个时候就需要自动映射了。Mybatis默认开启自动映射

DefaultResultSetHandler:

/**
 * 在 MyBatis 中,结果集自动映射有三种等级。这三种等级官方文档上有所说明,这里直
 * 接引用一下。如下:
 * NONE - 禁用自动映射。仅设置手动映射属性
 * PARTIAL - 将自动映射结果除了那些有内部定义内嵌结果映射的(joins)
 * FULL - 自动映射所有
 * 除了以上三种等级,我们还可以显示配置<resultMap>节点的 autoMapping 属性,以启用
 * 或者禁用指定 ResultMap 的自动映射设定。
 * @param resultMap
 * @param isNested
 * @return
 */
private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) {
  if (resultMap.getAutoMapping() != null) {//检查是否设置自动映射属性
    return resultMap.getAutoMapping();
  } else {
    // 对于嵌套 resultMap,仅当全局的映射行为为 FULL 时,才进行自动映射
    if (isNested) {
      return AutoMappingBehavior.FULL == configuration.getAutoMappingBehavior();
    } else {
      // 对于普通的 resultMap,只要全局的映射行为不为 NONE,即可进行自动映射
      return AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior();
    }
  }
}
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
  // 获取 UnMappedColumnAutoMapping 列表,即需要自动映射的自动信息
  List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
  boolean foundValues = false;
  if (!autoMapping.isEmpty()) {
    for (UnMappedColumnAutoMapping mapping : autoMapping) {
      // 根据column获取结果集中的值
      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<>();
    // 从 ResultSetWrapper 中获取未配置在 <resultMap> 中的列名
    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;
        }
      }
      // 将下划线形式的列名转成驼峰式,比如 AUTHOR_NAME -> authorName
      final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
      if (property != null && metaObject.hasSetter(property)) {
        if (resultMap.getMappedProperties().contains(property)) {
          // 检测当前属性是否存在于 resultMap 中
          continue;
        }
        final Class<?> propertyType = metaObject.getSetterType(property);
        if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
          //获取类型处理器
          final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
          //封装到UnMappedColumnAutoMapping
          autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
        } else {
          configuration.getAutoMappingUnknownColumnBehavior()
              .doAction(mappedStatement, columnName, property, propertyType);
        }
      } else {
        // 若 property 为空,或实体类中无 property 属性,此时无法完成
        // 列名与实体类属性建立映射关系。针对这种情况,有三种处理方式,
        // 1. 什么都不做
        // 2. 仅打印日志
        // 3. 抛出异常
        // 默认情况下,是什么都不做
        configuration.getAutoMappingUnknownColumnBehavior()
            .doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
      }
    }
    //写入缓存
    autoMappingsCache.put(mapKey, autoMapping);
  }
  return autoMapping;
}
ResultSetWrapper:
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;
}
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);
  final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);
  for (String columnName : columnNames) {//columnNames是结果集
    final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);
    if (mappedColumns.contains(upperColumnName)) {//已有映射关系,即在<resultMap>中有配置
      mappedColumnNames.add(upperColumnName);
    } else {//没有映射关系
      unmappedColumnNames.add(columnName);
    }
  }
  mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);
  unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);
}

       自动映射结束,接下来就是<resultMap>相关映射了,过程和自动映射基本相似

DefaultResultSetHandler:
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) {
      //获取resultMap 中 <result> column对应在 结果集中的值
      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')
        //将 value设置到 property 上
        metaObject.setValue(property, value);
      }
    }
  }
  return foundValues;
}

 四.总结

      本节结果映射只是分析了最简单的结果映射,对于更高级的嵌套结果映射并没有进行分析,大家自行分析吧。结果映射流程整体还是清晰的:包装结果集,创建返回对象,填充返回对象属性。

      下一章准备分析Mybatis的数据源,一起期待吧!

       结语:人生得意须尽欢,莫使金樽空对月

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值