Mybatis源码阅读(三):结果集映射3.1 —— ResultSetBuilder与简单映射

前言

在前面的文章中,已经介绍了三种SqlSource的代码,下面开始介绍执行select语句对查询结果集封装的过程。

ResultSetHandler

前面的文章中得知,mybatis会将结果集按照映射配置文件中定义的映射规则,如resultMap节点,映射成相应的结果对象。

在StatementHandler接口执行完指定的select语句后,会将查询结果集交给ResultSetHandler完成映射处理。

ResultSetHandler接口代码如下:

/**
 * 处理select查询的结果集
 * @author Clinton Begin
 */
public interface ResultSetHandler {

    /**
     * 处理结果集,生成结果集集合
     * @param stmt
     * @param <E>
     * @return
     * @throws SQLException
     */
    <E> List<E> handleResultSets(Statement stmt) throws SQLException;

    /**
     * 处理结果集,返回相应的游标
     * @param stmt
     * @param <E>
     * @return
     * @throws SQLException
     */
    <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

    /**
     * 处理存储过程
     * @param cs
     * @throws SQLException
     */
    void handleOutputParameters(CallableStatement cs) throws SQLException;

}

ResultSetHandler只有DefaultResultSetHandler一个实现,该类是处理结果集映射的核心类。核心字段如下所示:

 

public class DefaultResultSetHandler implements ResultSetHandler {

    private static final Object DEFERRED = new Object();

    /**
     * MyBatis执行器
     */
    private final Executor executor;
    private final Configuration configuration;
    /**
     * Sql节点
     */
    private final MappedStatement mappedStatement;
    /**
     * 游标
     */
    private final RowBounds rowBounds;
    /**
     * 参数处理器
     */
    private final ParameterHandler parameterHandler;
    /**
     * 结果集处理器
     */
    private final ResultHandler<?> resultHandler;
    /**
     * Sql对象
     */
    private final BoundSql boundSql;
    private final TypeHandlerRegistry typeHandlerRegistry;
    /**
     * 对象工厂和反射工厂
     */
    private final ObjectFactory objectFactory;
    private final ReflectorFactory reflectorFactory;

    /**
     * 映射缓存
     */
    private final Map<CacheKey, Object> nestedResultObjects = new HashMap<>();
    private final Map<String, Object> ancestorObjects = new HashMap<>();

    /**
     * 自动映射列缓存
     */
    private final Map<String, List<UnMappedColumnAutoMapping>> autoMappingsCache = new HashMap<>();

    /**
     * 记录是否使用构造器创建映射对象
     */
    private boolean useConstructorMappings;
}

handlerResultSets方法

通过select语句查询司机卡得到的结果集由handlerResultSets方法进行处理。该方法可以处理由Statement、PreparedStatement、CallableStatement产生的结果集。其中,Statement用于处理静态SQL,PrepareStatement用于处理预处理的SQL,CallableStatement用于处理存储过程,存储过程的结果集可能有多个,mybatis中对多结果集也进行了处理。由于java开发多数是mysql,而mysql中存储过程使用频率非常之少,因此这里不对多结果集进行讲解。

handleResultSets方法的代码如下。

 /**
     * ☆
     * select查询到的结果集会在这里被处理
     *
     * @param stmt
     * @return
     * @throws SQLException
     */
    @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);
        // 获取到sql节点所有的resultMap(一般只有一个)
        List<ResultMap> resultMaps = mappedStatement.getResultMaps();
        // resultMap的数量
        int resultMapCount = resultMaps.size();
        validateResultMapsCount(rsw, resultMapCount);
        // 遍历resultMaps
        while (rsw != null && resultMapCount > resultSetCount) {
            // 获取指定下标的resultMap
            ResultMap resultMap = resultMaps.get(resultSetCount);
            // 处理resultSet
            handleResultSet(rsw, resultMap, multipleResults, null);
            // 获取下一个结果集
            rsw = getNextResultSet(stmt);
            cleanUpAfterHandlingResultSet();
            resultSetCount++;
        }
        // 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);
    }

查询到的结果集可能有多个,mybatis默认先处理单结果集,getFirstResultSet方法用于获取第一个结果集对象。而getNextResultSet则是用于获取下一个结果集


    /**
     * 获取第一个结果集对象。
     * 在操作存储过程时,可能会得到多个结果集
     * 该方法只获取第一个结果集
     *
     * @param stmt
     * @return
     * @throws SQLException
     */
    private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
        // 获取结果集
        ResultSet rs = stmt.getResultSet();
        // 结果集如果为null就继续向下获取
        while (rs == null) {
            // 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;
    }

    /**
     * 获取下一个resultSet结果集
     *
     * @param stmt
     * @return
     */
    private ResultSetWrapper getNextResultSet(Statement stmt) {
        // Making this method tolerant of bad JDBC drivers
        try {
            // 检测jdbc是否支持多结果集
            if (stmt.getConnection().getMetaData().supportsMultipleResultSets()) {
                // 检测是否还存在需要处理的结果集
                if (!(!stmt.getMoreResults() && stmt.getUpdateCount() == -1)) {
                    ResultSet rs = stmt.getResultSet();
                    if (rs == null) {
                        return getNextResultSet(stmt);
                    } else {
                        return new ResultSetWrapper(rs, configuration);
                    }
                }
            }
        } catch (Exception e) {
            // Intentionally ignored.
        }
        return null;
    }

在上面的代码中,DefaultResultHandler在获取到结果集对象之后,会将其封装成ResultSetWrapper对象再进行处理。ResultSetWrapper对象中记录了结果集的一些元数据,并提供了一系列操作ResultSet的辅助方法,下面是ResultSetWrapper的核心字段。


/**
 * 对ResultSet进行封装
 * 存放了ResultSet的元数据
 *
 * @author Iwao AVE!
 */
public class ResultSetWrapper {

    /**
     * 查询得到的结果集
     */
    private final ResultSet resultSet;
    /**
     * 一堆类型处理器
     */
    private final TypeHandlerRegistry typeHandlerRegistry;
    /**
     * resultSet每列列名
     */
    private final List<String> columnNames = new ArrayList<>();
    /**
     * 每列对应的java类型
     */
    private final List<String> classNames = new ArrayList<>();
    /**
     * 每列对应的jdbc类型
     */
    private final List<JdbcType> jdbcTypes = new ArrayList<>();
    /**
     * key是列名,value是TypeHandler
     */
    private final Map<String, Map<Class<?>, TypeHandler<?>>> typeHandlerMap = new HashMap<>();
    /**
     * 记录被映射的列名
     */
    private final Map<String, List<String>> mappedColumnNamesMap = new HashMap<>();
    /**
     * 记录未映射的列名
     */
    private final Map<String, List<String>> unMappedColumnNamesMap = new HashMap<>();
}

在ResultSetWrapper的构造方法中,会初始化columnNames、jdbcTypes、classNames三个集合,代码如下。

    public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
        super();
        this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        this.resultSet = rs;
        // 获取resultSet的元信息
        final ResultSetMetaData metaData = rs.getMetaData();
        // 获取resultSet列数
        final int columnCount = metaData.getColumnCount();
        // 遍历每一列,封装 列名、jdbc类型、java类型
        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));
        }
    }

简单映射

介绍完整体的流程,下面来看handleResultSet方法。该方法的核心功能是完成对单个结果集的映射(即单表查询的映射)。代码如下。

    /**
     * 根据resultMap定义的映射规则去处理resultSet。并将映射的结果添加到multipleResults集合
     *
     * @param rsw
     * @param resultMap
     * @param multipleResults
     * @param parentMapping
     * @throws SQLException
     */
    private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
        try {
            if (parentMapping != null) {
                // 处理结果集中的嵌套映射。(resultMap中套着resultMap)
                handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
            } else {
                if (resultHandler == null) {
                    // 用户没有指定resultHandler,就用DefaultResultHandler
                    DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
                    // 对resultSet进行映射,并将映射结果添加到defaultResultHandler
                    handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
                    // 将defaultResultHandler中保存的集合添加到multipleResults
                    multipleResults.add(defaultResultHandler.getResultList());
                } else {
                    // 使用用户指定的resultHandler处理结果集
                    handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
                }
            }
        } finally {
            // issue #228 (close resultsets)
            closeResultSet(rsw.getResultSet());
        }
    }

该方法的核心代码就是handleRowValues。方法中判断是否包含嵌套映射去决定处理简单映射还是嵌套映射,代码如下。

    /**
     * 结果集映射核心方法
     *
     * @param rsw
     * @param resultMap
     * @param resultHandler
     * @param rowBounds
     * @param parentMapping
     * @throws SQLException
     */
    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);
        }
    }

下面先看简单映射。简单映射的核心代码在handleRowValuesForSimpleResultMap方法中,下面先分析该方法执行流程。

  1. 调用skipRows方法,根据RowBounds中的offset值定位到指定的记录行。RowBounds叫做游标,后面的文章会对其进行介绍。
  2. 调用shouldProcessMoreRows方法检测是否还有需要映射的记录。
  3. 通过resolveDiscriminatedResultMap方法确定映射要使用的ResultMap对象
  4. 调用getRowValue方法对Result中的一行记录进行映射。
    1. 通过createResultObject方法创建映射后的结果对象。
    2. 通过shouldApplyAutomaticMap平时方法检测是否开启了自动映射功能
    3. 通过applyAutomaiticMappings方法自动映射ResultMap中为明确映射的列
    4. 通过applyPropertyMap平时方法映射ResultMap中明确映射的列,到这里该行记录的数据已经完全映射到了结果对象的相应属性中。
  5. 调用storeObject方法保存映射得到的结果集对象

handleRowValuesForSimpleResultMap代码如下。

    /**
     * 简单结果集映射处理
     *
     * @param rsw
     * @param resultMap
     * @param resultHandler
     * @param rowBounds
     * @param parentMapping
     * @throws SQLException
     */
    private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
            throws SQLException {
        DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
        ResultSet resultSet = rsw.getResultSet();
        // 通过游标的offset值定位到指定的记录行
        skipRows(resultSet, rowBounds);
        // 检测是否还有需要映射的记录
        while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
            // 确定使用的ResultMap对象。多数情况下,这里指的还是传入的ResultMap
            ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
            // 映射
            Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
            // 保存映射结果
            storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
        }
    }

skipRows方法是根据RowBounds.offset字段的值定位到指定的记录。

    /**
     * 通过游标定位到指定行
     *
     * @param rs
     * @param rowBounds
     * @throws SQLException
     */
    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;
                }
            }
        }
    }

定位到指定的记录行之后,再通过shouldProcessMoreRows方法检测是否还有需要映射的行。

    /**
     * 检测是否还有需要映射的数据
     *
     * @param context
     * @param rowBounds
     * @return
     */
    private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) {
        return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();
    }

resolveDiscriminatedResultMap方法会根据ResultMap对象中记录的Discriminator以及参与映射的列值,选择映射操作最终使用的ResultMap,具体实现如下,。

    /**
     * 根据ResultMap中记录的Discriminator对象以及参与映射的记录行中的列值
     * 确定使用的ResultMap对象
     *
     * @param rs
     * @param resultMap
     * @param columnPrefix
     * @return
     * @throws SQLException
     */
    public ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) throws SQLException {
        Set<String> pastDiscriminators = new HashSet<>();
        // 通过discriminator标签去确定使用哪个ResultMap。使用不多,不进行注释
        Discriminator discriminator = resultMap.getDiscriminator();
        while (discriminator != null) {
            final Object value = getDiscriminatorValue(rs, discriminator, columnPrefix);
            final String discriminatedMapId = discriminator.getMapIdFor(String.valueOf(value));
            if (configuration.hasResultMap(discriminatedMapId)) {
                resultMap = configuration.getResultMap(discriminatedMapId);
                Discriminator lastDiscriminator = discriminator;
                discriminator = resultMap.getDiscriminator();
                if (discriminator == lastDiscriminator || !pastDiscriminators.add(discriminatedMapId)) {
                    break;
                }
            } else {
                break;
            }
        }
        return resultMap;
    }
    private Object getDiscriminatorValue(ResultSet rs, Discriminator discriminator, String columnPrefix) throws SQLException {
        final ResultMapping resultMapping = discriminator.getResultMapping();
        final TypeHandler<?> typeHandler = resultMapping.getTypeHandler();
        return typeHandler.getResult(rs, prependPrefix(resultMapping.getColumn(), columnPrefix));
    }

通过上面方法的处理确定了映射使用的ResultMap对象,之后会调用getRowValue完成对该记录的映射。首先根据ResultMap指定的类型创建对应的结果对象和MetaObject,再根据配置信息决定是否自动映射ResultMap中未明确映射的列,映射完毕后返回结果对象。代码如下。

    /**
     * 映射
     *
     * @param rsw
     * @param resultMap
     * @param columnPrefix
     * @return
     * @throws SQLException
     */
    private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
        // 与延迟加载有关
        final ResultLoaderMap lazyLoader = new ResultLoaderMap();
        // 创建该行记录映射之后的结果对象,就是resultMap的type属性指定的类
        Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
        if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
            // 创建上面对象的metaObject
            final MetaObject metaObject = configuration.newMetaObject(rowValue);
            // 成功映射任意属性,则为true,否则为false
            boolean foundValues = this.useConstructorMappings;
            // 检测是否需要自动映射
            if (shouldApplyAutomaticMappings(resultMap, false)) {
                // 自动映射resultMap未指定的列
                foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
            }
            // 映射resultMap指定的列
            foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
            foundValues = lazyLoader.size() > 0 || foundValues;
            // 如果没有映射任何属性,就根据mybatis-config.xml配置的returnInstanceForEmptyRow配置决定如何返回
            rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
        }
        return rowValue;
    }

该方法中,createResultObject方法负责创建数据库记录映射得到的结果对象,该方法会返回结果集的列数、constructorResultMappings集合等信息,选择不同的方式创建结果对象。具体实现如下。

    /**
     * 创建该行记录映射之后的结果对象,就是resultMap的type属性指定的类
     *
     * @param rsw
     * @param resultMap
     * @param lazyLoader
     * @param columnPrefix
     * @return
     * @throws SQLException
     */
    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);
        // TODO 如果包含嵌套查询且配置了延迟加载,就创建代理对象
        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;
    }

    /**
     * 创建映射结果对象
     *
     * @param rsw
     * @param resultMap
     * @param constructorArgTypes
     * @param constructorArgs
     * @param columnPrefix
     * @return
     * @throws SQLException
     */
    private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
            throws SQLException {
        // 根据resultMap配置的type属性去创建对应的MetaClass
        final Class<?> resultType = resultMap.getType();
        final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
        // 获取到constructor节点
        final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
        if (hasTypeHandlerForResultObject(rsw, resultType)) {
            // 结果集只有一列,并且存在TypeHandler对象可以将该列转换成resultType指定的值(Integer、String等)
            return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
        } else if (!constructorMappings.isEmpty()) {
            // resultMap中指定了constructor标签,通过反射方式调用构造方法创建对象
            return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
        } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
            // 使用默认的无参构造创建
            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);
    }

完成了对映射结果对象的创建后,下面就会将一行记录的各个列映射到该结果集对象的对应属性中。在成功创建对象并且获取到MetaObject之后,会调用shouldApplyAutomaticMappings方法检测是否允许自动映射,如果允许则调用applyAutomaiticMappings方法对ResultMap未指定的列进行自动映射。

    /**
     * 是否需要自动映射。
     *
     * @param resultMap
     * @param isNested
     * @return
     */
    private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) {
        if (resultMap.getAutoMapping() != null) {
            return resultMap.getAutoMapping();
        } else {
            if (isNested) {
                return AutoMappingBehavior.FULL == configuration.getAutoMappingBehavior();
            } else {
                return AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior();
            }
        }
    }

    /**
     * 自动映射未指定的列
     * @param rsw
     * @param resultMap
     * @param metaObject
     * @param columnPrefix
     * @return
     * @throws SQLException
     */
    private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
        // 查找需要自动映射的列
        List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
        boolean foundValues = false;
        if (!autoMapping.isEmpty()) {
            // 映射列不为空,一一映射
            for (UnMappedColumnAutoMapping mapping : autoMapping) {
                // 从resultSet获取值
                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;
    }

createAutomaticMappings方法负责为未映射的列查找对应的属性,并将二者关联起来封装成UnMappedColumnAutoMapping对象。createAutomaticMappings方法的具体实现如下。

    /**
     * 查找需要自动映射的列
     * @param rsw
     * @param resultMap
     * @param metaObject
     * @param columnPrefix
     * @return
     * @throws SQLException
     */
    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;
                    }
                }
                // 根据列名查找对应的属性
                final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
                // 属性不为空并且有set方法
                if (property != null && metaObject.hasSetter(property)) {
                    // 该列已经映射,不重复映射
                    if (resultMap.getMappedProperties().contains(property)) {
                        continue;
                    }
                    final Class<?> propertyType = metaObject.getSetterType(property);
                    if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
                        // 查找对应的TypeHandler对象
                        final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
                        // 将该列添加到autoMapping集合中
                        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;
    }

通过applyAutomaiticMappings方法处理完自动映射之后,后续会通过applyPropertyMappings方法对ResultMap中指定的列进行映射,核心代码如下。

    /**
     * 根据配置去映射
     * @param rsw
     * @param resultMap
     * @param metaObject
     * @param lazyLoader
     * @param columnPrefix
     * @return
     * @throws SQLException
     */
    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) {
            // 获取列名
            String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
            if (propertyMapping.getNestedResultMapId() != null) {
                // 判断该节点是否是对其他ResultMapping进行引用
                column = null;
            }
            // 场景1:column是{prop1=col1,prop2=col2}形式
            if (propertyMapping.isCompositeResult()
                    // 场景2:基本类型的属性映射
                    || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
                    // 场景3:多结果集处理
                    || propertyMapping.getResultSet() != null) {
                // 完成映射,得到属性值
                Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
                // 获取属性名称
                final String property = propertyMapping.getProperty();
                if (property == null) {
                    continue;
                } else if (value == DEFERRED) {
                    // 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;
    }

其中,映射操作是在getPropertyMappingValue方法中完成,具体代码如下,

    /**
     * 完成映射操作并获取属性值
     * @param rs
     * @param metaResultObject
     * @param propertyMapping
     * @param lazyLoader
     * @param columnPrefix
     * @return
     * @throws SQLException
     */
    private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
            throws SQLException {
        if (propertyMapping.getNestedQueryId() != null) {
            // 存在嵌套查询
            return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
        } else if (propertyMapping.getResultSet() != null) {
            // 多结果集的处理
            addPendingChildRelation(rs, metaResultObject, propertyMapping);
            return DEFERRED;
        } else {
            final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
            final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
            // 使用typeHandler来获取属性值
            return typeHandler.getResult(rs, column);
        }
    }

到这里,已经得到了一个完整映射的结果对象,之后DefaultResultSetHandler会通过storeObject方法将该结果对象保存到合适的位置,这样该行记录就完成了。如果是嵌套映射或者嵌套查询的结果对象则保存到父对象对应的属性中,如果是简单映射则保存到ResultHandler中。

    /**
     * 保存映射结果
     * @param resultHandler
     * @param resultContext
     * @param rowValue
     * @param parentMapping
     * @param rs
     * @throws SQLException
     */
    private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
        if (parentMapping != null) {
            // 嵌套映射,保存在父对象属性中
            linkToParents(rs, parentMapping, rowValue);
        } else {
            // 普通映射,保存在ResultHandler
            callResultHandler(resultHandler, resultContext, rowValue);
        }
    }

    private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
        resultContext.nextResultObject(rowValue);
        ((ResultHandler<Object>) resultHandler).handleResult(resultContext);
    }

至此,简单映射的流程就介绍完了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章