Mybatis3源码分析(16)-Sql解析执行-结果集映射(ResultSetHandler)

撸了今年阿里、腾讯和美团的面试,我有一个重要发现…….

作者:ashan_li

出处:https://blog.csdn.net/ashan_li/article/category/6047775/1?


在PreparedStatementHandler中的query()方法中,是用ResultSetHandler来完成结果集的映射的。

     public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        return resultSetHandler.<E> handleResultSets(ps);
      }

Mybatis中只提供了一个ResultSetHandler的实现,那就是DefaultResultSetHandler。下面来看看他的handleResultSets()方法

    public List<Object> handleResultSets(Statement stmt) throws SQLException {
        final List<Object> multipleResults = new ArrayList<Object>();

        int resultSetCount = 0;
        //获取第一个ResultSet,通常只会有一个
        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);
          //完成映射,将结果加到入multipleResults中
          handleResultSet(rsw, resultMap, multipleResults, null);
          rsw = getNextResultSet(stmt);
          cleanUpAfterHandlingResultSet();
          resultSetCount++;
        }

        String[] resultSets = mappedStatement.getResulSets();
        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);
      }

在实际运行过程中,通常情况下一个Sql语句只返回一个结果集,对多个结果集的情况不做分析 。实际很少用到。

继续看handleResultSet方法

    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 {
            //一般情况resultHandler都为空,见ResultHandler.NO_RESULT_HANDLER
            if (resultHandler == null) {
              DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
              //生成对象,并加到defaultResultHandler.resultList集合中
              handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
              //将结果加入multipleResults中
              multipleResults.add(defaultResultHandler.getResultList());
            } else {
              handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
            }
          }
        } finally {
          //关闭结果集
          closeResultSet(rsw.getResultSet()); // issue #228 (close resultsets)
        }
      }
    private 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);
        }
      }  

简单映射handleRowValuesForSimpleResultMap

    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(rsw.getResultSet(), resultContext, rowBounds)) {
          //discriminator的处理,可以根据条件选择不同的映射
          ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
          //真正从ResultSet中映射出一个对象
          Object rowValue = getRowValue(rsw, discriminatedResultMap);
          //加入resultHandler.resultList中
          storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
        }
      }
       //没有内映射
       private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
        final ResultLoaderMap lazyLoader = new ResultLoaderMap();
        //实例化一个对象,类型为resultMap.getType(),最终调用了ObjectFactory.create()方法
        Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null);
        if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
          //设置对象属性
          final MetaObject metaObject = configuration.newMetaObject(resultObject);
          boolean foundValues = resultMap.getConstructorResultMappings().size() > 0;
          if (shouldApplyAutomaticMappings(resultMap, !AutoMappingBehavior.NONE.equals(configuration.getAutoMappingBehavior()))) {
            //自动映射,结果集中有的column,但resultMap中并没有配置   
            foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
          }
          //映射result节点
          foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
          foundValues = lazyLoader.size() > 0 || foundValues;
          resultObject = foundValues ? resultObject : null;
          return resultObject;
        }
        return resultObject;
      }

以上代码开始总结出简单映射(没有内映射)的逻辑

  1. 每条结果都会生成一个java对象
  2. 根据构造方法实例化对象
  3. 自动映射(结果集有但在resultMap里没有配置的字段),有两情况会发生自动映射

    1. 在resultMap上配置了autoMapping=”true”属性
    2. 在mybatis-config.xml配置了autoMappingBehavior=”PARTIAL|FULL”,默认为PARTIAL。

    在实际应用中,都会使用自动映射,减少配置的工作。自动映射在Mybatis中也是默认开启的。

  4. 最后再映射属性。

根据构造方法实例化对象

    <pre name="code" class="java">private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
        //构造方法中的参数类型
        final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
        //构造方法中具体值
        final List<Object> constructorArgs = new ArrayList<Object>();
        //根据构造方法生成对象
        final Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
        if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
          final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
          for (ResultMapping propertyMapping : propertyMappings) {
            if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) { // issue gcode #109 && issue #149
              return configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
            }
          }
        }
        return resultObject;
      }

      private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
          throws SQLException {
        final Class<?> resultType = resultMap.getType();
        //resultMap配置中的construnctor节点
        final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
        if (typeHandlerRegistry.hasTypeHandler(resultType)) {
          return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
        } else if (constructorMappings.size() > 0) {
          //construnctor节点有配置
          return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
        } else {
         //construnctor节点没有配置,调用无参的构造方法
          return objectFactory.create(resultType);
        }
      }

      private Object createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType, List<ResultMapping> constructorMappings, List<Class<?>> constructorArgTypes,
          List<Object> constructorArgs, String columnPrefix) throws SQLException {
        boolean foundValues = false;
        for (ResultMapping constructorMapping : constructorMappings) {
          final Class<?> parameterType = constructorMapping.getJavaType();
          final String column = constructorMapping.getColumn();
          final Object value;
         //取出参数类型和具体的值
          if (constructorMapping.getNestedQueryId() != null) {
            value = getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix);
          } else if (constructorMapping.getNestedResultMapId() != null) {
            final ResultMap resultMap = configuration.getResultMap(constructorMapping.getNestedResultMapId());
            value = getRowValue(rsw, resultMap);
          } else {
            final TypeHandler<?> typeHandler = constructorMapping.getTypeHandler();
            value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(column, columnPrefix));
          }
          constructorArgTypes.add(parameterType);
          constructorArgs.add(value);
          foundValues = value != null || foundValues;
        }
         //创建对象
        return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
      }

自动映射

      private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
        //获取结果集中在resultMap中没有配置的列名
        //如果resultMap中只设置了resultType="java.util.HashMap"的话,全都会在这里完成映射
        final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
        boolean foundValues = false;
        for (String columnName : unmappedColumnNames) {
          //属性名就是列名
          String propertyName = columnName;
          if (columnPrefix != null && columnPrefix.length() > 0) {
            // When columnPrefix is specified,
            // ignore columns without the prefix.
            if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
              propertyName = columnName.substring(columnPrefix.length());
            } else {
              continue;
            }
          }
          //是否有对应的属性
          final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
          //是否有对应的set方法
          if (property != null && metaObject.hasSetter(property)) {
            final Class<?> propertyType = metaObject.getSetterType(property);
            if (typeHandlerRegistry.hasTypeHandler(propertyType)) {
              final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
              final Object value = typeHandler.getResult(rsw.getResultSet(), columnName);
              if (value != null || configuration.isCallSettersOnNulls()) { // issue #377, call setter on nulls
                if (value != null || !propertyType.isPrimitive()) {
                  //直接设置
                  metaObject.setValue(property, value);
                }
                foundValues = true;
              }
            }
          }
        }
        return foundValues;
      }

映射result节点

     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;
        //获取需要映射的ResultMapping
        final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
        for (ResultMapping propertyMapping : propertyMappings) {
          final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
          if (propertyMapping.isCompositeResult()
              || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
              || propertyMapping.getResultSet() != null) {
            //在结果中的获取对应的值
            Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
            final String property = propertyMapping.getProperty(); // issue #541 make property optional
            if (value != NO_VALUE && property != null && (value != null || configuration.isCallSettersOnNulls())) { // issue #377, call setter on nulls
              if (value != null || !metaObject.getSetterType(property).isPrimitive()) {
                //设置属性
                metaObject.setValue(property, value);
              }
              foundValues = true;
            }
          }
        }
        return foundValues;
      }
     private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
          throws SQLException {
        if (propertyMapping.getNestedQueryId() != null) {
          //子查询,这里就是会产生N+1次查询的地方,每个记录都会再执行一个子查询。子查询的过程这里就不在讨论了。
          return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
        } else if (propertyMapping.getResultSet() != null) {
          addPendingChildRelation(rs, metaResultObject, propertyMapping);
          return NO_VALUE;
        } else if (propertyMapping.getNestedResultMapId() != null) {
          // the user added a column attribute to a nested result map, ignore it
          return NO_VALUE;
        } else {
          final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
          final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
          //直接从结果集里获取。
          return typeHandler.getResult(rs, column);
        }
      }

复杂映射(内映射)handleRowValuesForNestedResultMap

处理这种映射的逻辑比较复杂。这里先举例说明:

配置如下resultMap

    <resultMap type="com.ashan.mybatis.User" id="detailUserResultMap"><!-- 整个resultMap会被解析成一个ResultMap对应 -->
            <constructor>
                <idArg column="user_id" javaType="String"/><!-- idArg会被解析成一个resultMapping对象 -->
                <arg column="user_name" javaType="String"/><!-- resultMapping对象 -->
            </constructor>

            <result property="svcnum" column="svc_num" /> <!-- resultMapping对象 -->

            <association property="cust" javaType="com.ashan.mybatis.Cust"> <!-- resultMapping对象 这个resultMapping对象指向了另一个ResultMap-->
                <id property="id" column="cust_id"/>
                <result property="custname" column="cust_name"/>
                <result property="certNo" column="cert_no"/>
            </association>

            <collection property="accts" ofType="com.ashan.mybatis.Acct">
                <id property="id" column="acct_id" />
                <result property="payName" column="pay_name"/>
                <result property="bankNo" column="bank_no"/>
            </collection>

        </resultMap>

假设需要映射的结果集:

2019080910016_1.png
可以看出,这个结果集将最终会映射成两个对象User对象,两个User对象对应的Cust对应都是cust_01,id为user_01对应的accts为:acct_01,acct_02,acct_04;user_02对应的acct只有一个acct_03。

User对象映射过程

下面来看Mybatis是怎么生成这两个User对象的。

    private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
        final DefaultResultContext resultContext = new DefaultResultContext();
        skipRows(rsw.getResultSet(), rowBounds);
        Object rowValue = null;
        while (shouldProcessMoreRows(rsw.getResultSet(), resultContext, rowBounds)) {

          //开始处理一行新的结果集记录      
          final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);

          //为这行记录生成一个key,createRowKey方法会利用idResultMapping(即idArg,和id节点)和resultMap的id来生成key
          //例子中第1、2、4条记录生成的key都是一样的,大概内容为detailUserResultMap:user_01
          //第3条记录生成的key大概内容为detailUserResultMap:user_02
          final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);
          //nestedResultObjects是一个HashMap对象,在映射过程中所有生成的映射对象(包括内映射对象),都会生成一个key并保存在这里。
          //例子中生成映射对象有三类:User,Cust,Acct

          Object partialObject = nestedResultObjects.get(rowKey);
          //如果是处理第1、3条记录,这里的parialObject为null值
          //如果是处理第2、4条记录,这里的parialObject不为null
          //
          if (mappedStatement.isResultOrdered()) { // issue #577 && #542
            //先不讨论
            if (partialObject == null && rowValue != null) {
              nestedResultObjects.clear();
              storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
            }
            rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, rowKey, null, partialObject);
          } else {
            //这个方法把结果集的记录映射成java对象
            //处理第一条记录时,rowValue是新生成的User对象(user_01)其中属性cust为cust_01,accts里只有一个acct_01
            //处理第二条记录时,rowValue对象就是第一条记录生成里的User对象,不过这时accts里多了一条acct_02
            //处理第三条记录时,rowValue为新生成的User对象(user_02),cust属性为cust_01,accts只有一个acct_03
            //处理第四条记录时,rowValue对象就是第一条记录生成里的User对象,这时accts里又多了一条acct_04
            rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, rowKey, null, partialObject);
            //只有第一条记录和第三条记录partialObject才会为null
            if (partialObject == null) {
              //把User对象加入到resultHandler.resultList中,这里也可以看出,虽然有四条记录,但只会被映射成两个User对象
              storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
            }
          }
        }
        if (rowValue != null && mappedStatement.isResultOrdered()) {
          storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
        }
      }

接下来继续看User对象的生成过程

     private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, CacheKey absoluteKey, String columnPrefix, Object partialObject) throws SQLException {
        final String resultMapId = resultMap.getId();
        Object resultObject = partialObject;
        //第一和第三条记录时,partialObject为null, resultObject也为null
        if (resultObject != null) {
          //处理第二、四条记录里会执行这里
          final MetaObject metaObject = configuration.newMetaObject(resultObject);
          putAncestor(absoluteKey, resultObject, resultMapId, columnPrefix);
          //直接调用内映射,即设置处理cust和accts,例子中主要是加入一个acct,因为cust只有一个,在user对象创建里就会被创建
          applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
          ancestorObjects.remove(absoluteKey);
        } else {
          //处理第一、三条记录里会执行这里,说明需要创建一个新的User对象
          final ResultLoaderMap lazyLoader = new ResultLoaderMap();
          //创建一个user对象,跟简单映射的处理方式一样
          resultObject = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
          if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
            final MetaObject metaObject = configuration.newMetaObject(resultObject);
            boolean foundValues = resultMap.getConstructorResultMappings().size() > 0;
            if (shouldApplyAutomaticMappings(resultMap, AutoMappingBehavior.FULL.equals(configuration.getAutoMappingBehavior()))) {
              //自动映射,跟简单映射的处理方式一样,跟简单映射的处理方式一样,例子中不会执行这一步
              foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
            }   
            //映射reulst节点,跟简单映射的处理方式一样,跟简单映射的处理方式一样 ,例子中主要映射svc_num    
            foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
            putAncestor(absoluteKey, resultObject, resultMapId, columnPrefix);
            //调用内映射,即生成cust和acct对象并设置到User对象中
            foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
            ancestorObjects.remove(absoluteKey);
            foundValues = lazyLoader.size() > 0 || foundValues;
            resultObject = foundValues ? resultObject : null;
          }
          //注意这里,生成User对象里combinedKey就是User对象的key,将新创建的两个User对象加入nestedResultObjects中,以便后续处理使用,在处理第二、四条记录里就可以使用对应的User对象了。
          if (combinedKey != CacheKey.NULL_CACHE_KEY) nestedResultObjects.put(combinedKey, resultObject);
        }
        return resultObject;
      }

上面是User对象的生成过程。nestedResultObjects在处理过程的作用很重要,由这个容器来控制是否需要创建新的User对象。

Cust对象映射过程

再来看Cust对象是怎么生成并加入到User对象中的

      //处理内映射,生成内映射对象,并加入到上层对象中。这里主要根据例子分析Cust对象的生成,上层对象为User
     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) {
             //resultMap中的collection,association节点都会被生成一个nestedResultMap,这里分析Cust对象,也就是association
            try {
              final String columnPrefix = getColumnPrefix(parentPrefix, resultMapping);
              //获取内映射的ResultMap
              final ResultMap nestedResultMap = getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix);
              CacheKey rowKey = null;
              Object ancestorObject = null;
              if (ancestorColumnPrefix.containsKey(nestedResultMapId)) {
                //第二、三、四记录时会执行,ancestorColumnPrefi也是一个HashMap,保存的是什么内容下面再看
                //第一条记录时,肯定不会有这个对应关系
                rowKey = createRowKey(nestedResultMap, rsw, ancestorColumnPrefix.get(nestedResultMapId));

                //这个ancestorObjects是一个HashMap跟名字一样只会保存原始对应,也就是上层对象,这里cust和acct对象是最下层的对象了。也就是说cust和acct没有内映射了
                //所以例子中ancestorObjects只会保留User对象
                ancestorObject = ancestorObjects.get(rowKey);
              }
              if (ancestorObject != null) {
                if (newObject) metaObject.setValue(resultMapping.getProperty(), ancestorObject);
              } else {
                //映射四条记录的Cust对应都会执行这里
                //这里是生成内映射Cust的key,大概是这样的:Cust:cust_01
                rowKey = createRowKey(nestedResultMap, rsw, columnPrefix);

                //这里的combineKeys是跟上层对象组合成的一个key
                //parentRowKey为上层对象(User对象)的key,第一、二、四条记录为user_01,第三条记录为user_02
                //这样combineKey值大概为:第一、二、四条记录为user_01:cust_01,第三条记录为user_02:cust_01
                final CacheKey combinedKey = combineKeys(rowKey, parentRowKey);   

                //从nestedResultObjects获取
                //第一、三条记录rowValue是为null的,第二、四条记录与第一条记录的combinedKey一样,所以rowValue的值不一样         
                Object rowValue = nestedResultObjects.get(combinedKey);
                boolean knownValue = (rowValue != null);
                //检查要映射的对象是否为Collection类型,这里是Cust类型,collectionProperty为null
                final Object collectionProperty = instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);            
                if (anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw.getResultSet())) {
                  //生成Cust对象
                  //注意入参的rowValue,第一、三条记录为null
                  rowValue = getRowValue(rsw, nestedResultMap, combinedKey, rowKey, columnPrefix, rowValue);
                  if (rowValue != null && !knownValue) {
                    //第一、三条记录时才会执行这里,第二、四条记录用的是第一条中的Cust对象,不用重复设置                 

                    if (collectionProperty != null) {
                      //User.cust属性不是集合,不会执行这里
                      final MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty);
                      targetMetaObject.add(rowValue);
                    } else {
                      //将生成的Cust对象设置到User对象中
                      metaObject.setValue(resultMapping.getProperty(), rowValue);
                    }
                    foundValues = true;
                  }
                }
              }
            } catch (SQLException e) {
              throw new ExecutorException("Error getting nested result map values for '" + resultMapping.getProperty() + "'.  Cause: " + e, e);
            }
          }
        }
        return foundValues;
      }

再来看getRowValue是怎么处理Cust对象的,这个getRowValue上前讲Cust对象时的代码一样的,只不过这次是对Cust对象为分析

      private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, CacheKey absoluteKey, String columnPrefix, Object partialObject) throws SQLException {
        //partialObject,第一、三条为null,第二、四条用是的第一条里的Cust,不为null
        final String resultMapId = resultMap.getId();
        Object resultObject = partialObject;
        if (resultObject != null) {
          //第二、四条记录
          final MetaObject metaObject = configuration.newMetaObject(resultObject);
          putAncestor(absoluteKey, resultObject, resultMapId, columnPrefix);
          //这个时间只需要处理Cust里的内映射就行了,例子中Cust没有内映射,这里将什么都不会发生
          //相当对处理第二、四条记录时,这个方法什么都没做
          applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
          ancestorObjects.remove(absoluteKey);
        } else {
          //第一、三条记录
          final ResultLoaderMap lazyLoader = new ResultLoaderMap();
          //实例化
          resultObject = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
          if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
            final MetaObject metaObject = configuration.newMetaObject(resultObject);
            boolean foundValues = resultMap.getConstructorResultMappings().size() > 0;
            if (shouldApplyAutomaticMappings(resultMap, AutoMappingBehavior.FULL.equals(configuration.getAutoMappingBehavior()))) {
              //自动映射
              foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
            }        
            //映射result节点
            foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
            putAncestor(absoluteKey, resultObject, resultMapId, columnPrefix);
            //内映射,例子中Cust没有内映射
            foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
            ancestorObjects.remove(absoluteKey);
            foundValues = lazyLoader.size() > 0 || foundValues;
            resultObject = foundValues ? resultObject : null;
          }
          //将新创建的Cust对象加入nestedResultObjects中
          if (combinedKey != CacheKey.NULL_CACHE_KEY) nestedResultObjects.put(combinedKey, resultObject);
        }
        return resultObject;
      }

从上面的代码可以看出,虽然四条记录对应的cust_id都为cust_01,按一般的ORM映射来说,在内存中四个User对象的Cust属性应该是同一个,但在这里个例子中会生成两个Cust对象。这是因为nestedResltOjects是用CombineKey,至于为什么这样做,还不知道!

Acct对象映射过程

Acct对象映射过程,还是applyNestedResultMapping方法

    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);
              final ResultMap nestedResultMap = getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix);
              CacheKey rowKey = null;
              Object ancestorObject = null;
              if (ancestorColumnPrefix.containsKey(nestedResultMapId)) {
                rowKey = createRowKey(nestedResultMap, rsw, ancestorColumnPrefix.get(nestedResultMapId));
                ancestorObject = ancestorObjects.get(rowKey);
              }
              if (ancestorObject != null) {
                if (newObject) metaObject.setValue(resultMapping.getProperty(), ancestorObject);
              } else {
                //四条记录都会执行这里
                //四条记录都生成不同的rowKey的,大概为acct_01,acct_02,acct_03,acct_04
                rowKey = createRowKey(nestedResultMap, rsw, columnPrefix);

                //parentRowKey为User对象的key
                //四条记录的combinedKey大概为user_01:acct_01,user_01:acct_02,user_02:acct_03,user_01:acct_04
                final CacheKey combinedKey = combineKeys(rowKey, parentRowKey);            
                Object rowValue = nestedResultObjects.get(combinedKey);
                boolean knownValue = (rowValue != null);

                //实例化集合属性
                //这里User.accts对象为一个集体,instantiateCollectionPropertyIfAppropriate方法会取出accts属性的值,如果为null则创建一个,并设置到User对象中
                //第一、二、四条记录返回的都是同一个,因为他们对应同一个User对象
                final Object collectionProperty = instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);            
                if (anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw.getResultSet())) {
                  //生成Acct对象
                  rowValue = getRowValue(rsw, nestedResultMap, combinedKey, rowKey, columnPrefix, rowValue);
                  if (rowValue != null && !knownValue) {
                    if (collectionProperty != null) {
                      final MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty);
                      //加入到集合中
                      targetMetaObject.add(rowValue);
                    } else {
                      metaObject.setValue(resultMapping.getProperty(), rowValue);
                    }
                    foundValues = true;
                  }
                }
              }
            } catch (SQLException e) {
              throw new ExecutorException("Error getting nested result map values for '" + resultMapping.getProperty() + "'.  Cause: " + e, e);
            }
          }
        }
        return foundValues;
      }
      private Object instantiateCollectionPropertyIfAppropriate(ResultMapping resultMapping, MetaObject metaObject) {
        //属性名,这里为accts
        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);

              //设置到User对象中,即User.setAccts(list)方法
              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;
      }

小结

到此,Mybatis是怎么利用ResultSet生成对象的过程已经分析完毕。分为简单映射和复杂映射。

简单映射就是不包含内映射的resultMap

复杂映射就是包含内映射的resultMap。

复杂映射的过程比较复杂,源代码也没有一行注释,本人是写了个实例,再通过eclipse中的debuger一步步来分析的。

赞(1) 打赏

如未加特殊说明,此网站文章均为原创,转载必须注明出处。Java 技术驿站 » Mybatis3源码分析(16)-Sql解析执行-结果集映射(ResultSetHandler)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

关注【Java 技术驿站】公众号,每天早上 8:10 为你推送一篇技术文章

扫描二维码关注我!


关注【Java 技术驿站】公众号 回复 “VIP”,获取 VIP 地址永久关闭弹出窗口

免费获取资源

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏