• MyBatis 源码分析之 Select 语句执行(下)


    • 三哥

    内容来自【自学星球】

    欢迎大家来了解我的星球,和星主(也就是我)一起学习 Java ,深入 Java 体系中的所有技术。我给自己定的时间是一年,无论结果如何,必定能给星球中的各位带来点东西。

    想要了解更多,欢迎访问👉:自学星球

    --------------SSM系列源码文章及视频导航--------------

    创作不易,望三连支持!

    SSM源码解析视频

    👉点我

    Spring

    1. Spring 中注入 Bean 的各种骚操作做
    2. Spring 中Bean的生命周期及后置处理器使用
    3. Spring 中容器启动分析之refresh方法执行之前
    4. Spring refresh 方法分析之一
    5. Spring refresh 方法之二 invokeBeanFactoryPostProcessors 方法解析
    6. Spring refresh 方法分析之三
    7. Spring refresh 方法之四 finishBeanFactoryInitialization 分析
    8. Spring AOP源码分析一
    9. Spring AOP源码分析二
    10. Spring 事务源码分析

    SpringMVC

    1. SpringMVC 启动流程源码分析
    2. SpringMVC 请求流程源码分析

    MyBatis

    1. MyBatis 源码分析之 SqlSessionFactory 创建
    2. MyBatis 源码分析之 SqlSession 创建
    3. MyBatis 源码分析之 Mapper 接口代理对象生成及方法执行
    4. MyBatis 源码分析之 Select 语句执行(上)
    5. MyBatis 源码分析之 Select 语句执行(下)
    6. MyBatis 源码分析一二级缓存

    ---------------------【End】--------------------

    一、结果集 ResultSet 自动映射成实体类对象

    回到下面代码:

    return resultSetHandler. handleResultSets(ps);

    //进行resultSet自动映射
    return resultSetHandler.<E> handleResultSets(ps);
    
    • 1
    • 2

    在分析代码之前,我们先来看看 ResultSetHandler 和 ResultSetWrapper 两个类。

    • ResultSetHandler对查询结果进行处理
    • ResultSetWrapper 对ResultSet进行包装,可以方便地从ResultSet中访问特定的列
    public interface ResultSetHandler {
        // 这里对结果集进行处理,返回的是集合
        <E> List<E> handleResultSets(Statement stmt) throws SQLException;
        // 这里对结果集进行处理,返回的是游标
        <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
        // 对存储过程的输出参数进行处理
        void handleOutputParameters(CallableStatement cs) throws SQLException;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    public class ResultSetWrapper {
    
        // jdbc返回的结果集
        private final ResultSet resultSet;
        // 类型转换器注册中心
        private final TypeHandlerRegistry typeHandlerRegistry;
        // 查询结果中每列的名称
        private final List<String> columnNames = new ArrayList<>();
        // 查询结果中每列的java类型
        private final List<String> classNames = new ArrayList<>();
        // 查询结果中每列的jdbc类型
        private final List<JdbcType> jdbcTypes = new ArrayList<>();
        // 每列对应的typeHandler
        private final Map<String, Map<Class<?>, TypeHandler<?>>> typeHandlerMap = new HashMap<>();
        // 记录了总的映射列名,key是ResultMap的id,value是该ResultMap的列名集合
        private final Map<String, List<String>> mappedColumnNamesMap = new HashMap<>();
        // 和上面的属性相反,记录了未映射的列名,key是ResultMap的id,value是该ResultMap未映射的列名集合
        private final Map<String, List<String>> unMappedColumnNamesMap = new HashMap<>();
    
    
        public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
            super();
            this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            this.resultSet = rs;
            final ResultSetMetaData metaData = rs.getMetaData();
            final int columnCount = metaData.getColumnCount();
            for (int i = 1; i <= columnCount; i++) {
                // columnLable代表原始的列名,columnName代表别名
                columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));
                jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
                classNames.add(metaData.getColumnClassName(i));
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    接着我们来看映射结果集方法源码:

    org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSets

    public List<Object> handleResultSets(Statement stmt) throws SQLException {
        ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
    
        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 resultMap = resultMaps.get(resultSetCount);
            // 处理结果集
            handleResultSet(rsw, resultMap, multipleResults, null);
            // 获取结果集的下一个结果
            rsw = getNextResultSet(stmt);
            // 清空嵌套resultMap
            cleanUpAfterHandlingResultSet();
            resultSetCount++;
        }
    
        // resultSets是针对多结果集的情况下,给每个结果集一个名称,多个名称之间使用,分割
        String[] resultSets = mappedStatement.getResultSets();
        if (resultSets != null) {
            // 和resultMaps的遍历处理类似
            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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    多结果集只会在调用存储的时候出现,所以这里只介绍下单个结果集的情况,即 handleResultSet(rsw, resultMap, multipleResults, null) 方法。

    1.1 结果集处理

    org.apache.ibatis.executor.resultset.DefaultResultSetHandler#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 {
                if (resultHandler == null) {
                    // 如果用户没有指定对结果的处理器ResultHandler,那么默认会使用DefaultResultHandler
                    DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
                    // 对结果集进行转换
                    handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
                    // 将解析后的结果添加到multiResults中
                    // 如果没有指定ResultHandler,那么默认会将解析之后的结果添加到multipleResults中
                    multipleResults.add(defaultResultHandler.getResultList());
                } else {
                    // 用户定义了对结果集的处理方法,即ResultHandler
                    // 那么使用ResultSetHandler处理之后,会将结果再交给ResultHandler进行处理
                    handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
                }
            }
        } finally {
            // issue #228 (close resultsets)
            closeResultSet(rsw.getResultSet());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    该方法通过 handleRowValues 方法来映射 ResultSet 结果,并将并将映射的结果从 defaultResultHandler 的 ResultList 方法中取出存入 multipleResults 中,完成映射。

    下面我们来看核心方法 handleRowValues 。

    org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleRowValues

    public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
        // 判断是否有嵌套的映射
        if (resultMap.hasNestedResultMaps()) {
            ensureNoRowBounds();
            checkResultHandler();
            // 处理嵌套映射
            handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        } else {
            // 处理简单映射
            handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    我们可以通过 resultMap.hasNestedResultMaps() 知道查询语句是否是嵌套查询,如果 resultMap 中包含 和 且其 select 属性不为空,则为嵌套查询。

    这里我们之分析简单查询。

    1.2 简单查询映射

    org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleRowValuesForSimpleResultMap

    private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
        throws SQLException {
        // ResultContext用来存放结果对象
        DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
        // 根据 RowBounds 定位到指定行记录(取出rowbounds中的offset,跳过结果集中的前面offset行)
        skipRows(rsw.getResultSet(), rowBounds);
        // ResultSet是一个集合,很有可能我们查询的就是一个List,这就就每条数据遍历处理
        while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
            ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
            // 从 resultSet 中获取结果
            Object rowValue = getRowValue(rsw, discriminatedResultMap);
            // 存储结果到resultHandler的ResultList,最后ResultList加入multipleResults中返回
            storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    该方法通过遍历结果集挨个调用 getRowValue 方法来进行结果集的映射,这里遍历映射是因为结果集可能不止一个。

    RowBounds 是默认的分页工具(内存分页)。

    下面我们来看看 getRowValue 方法的具体映射源码。

    org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getRowValue(org.apache.ibatis.executor.resultset.ResultSetWrapper, org.apache.ibatis.mapping.ResultMap)

    private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
        // 这个Map是用来存储延迟加载的BountSql的,我们下面来看
        final ResultLoaderMap lazyLoader = new ResultLoaderMap();
        // 创建实体类对象,比如 User 对象
        Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
         // 判断结果值是否为空,并且没有对应当前结果java类型的typehandler
        if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
            final MetaObject metaObject = configuration.newMetaObject(rowValue);
            boolean foundValues = this.useConstructorMappings;
            if (shouldApplyAutomaticMappings(resultMap, false)) {
                //自动映射,结果集中有的column,但resultMap中并没有配置  
                foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
            }
            // 根据  节点中配置的映射关系进行映射
            foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
            foundValues = lazyLoader.size() > 0 || foundValues;
            rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
        }
        return rowValue;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    该方法的主要逻辑分为如下几个步骤:

    1. 创建实体类对象。
    2. 自动映射结果集中有的 column,但 resultMap 中并没有配置。
    3. 根据 节点中配置的映射关系进行映射。

    1.2.1 创建实体

    下面我们来看看结果集映射对象的创建。

    org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getRowValue(org.apache.ibatis.executor.resultset.ResultSetWrapper, org.apache.ibatis.mapping.ResultMap)

    Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
    
    • 1

    org.apache.ibatis.executor.resultset.DefaultResultSetHandler#createResultObject(org.apache.ibatis.executor.resultset.ResultSetWrapper, org.apache.ibatis.mapping.ResultMap, org.apache.ibatis.executor.loader.ResultLoaderMap, java.lang.String)

    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<Class<?>>();
        final List<Object> constructorArgs = new ArrayList<Object>();
    
        // 调用重载方法创建实体类对象
        Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
        // 当前结果不为空,并且不存在可以直接将ResultSet转换为指定java类型的typeHandler
        if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
            final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
            for (ResultMapping propertyMapping : propertyMappings) {
                // issue gcode #109 && issue #149
                // 如果开启了延迟加载,则为 resultObject 生成代理类,如果仅仅是配置的关联查询,没有开启延迟加载,是不会创建代理类
                if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
                    /*
                         * 创建代理类,默认使用 Javassist 框架生成代理类。
                         * 由于实体类通常不会实现接口,所以不能使用 JDK 动态代理 API 为实体类生成代理。
                         * 并且将lazyLoader传进去了
                         */
                    resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
                    break;
                }
            }
        }
        // 如果结果对象不为空,并且构造方法使用到了构造参数映射,那么将useConstructorMappings设置为true
        this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
        return resultObject;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    下面重载方法 createResultObject 的实现逻辑

    org.apache.ibatis.executor.resultset.DefaultResultSetHandler#createResultObject(org.apache.ibatis.executor.resultset.ResultSetWrapper, org.apache.ibatis.mapping.ResultMap, java.util.List>, java.util.List, java.lang.String)

    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);
        // 取出构造函数参数的映射,就是FLAG为CONSTRUCTOR的映射
        final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
        if (hasTypeHandlerForResultObject(rsw, resultType)) {
            // 如果符合当前java结果类型的TypeHandler,那么会使用typehandler来对结果集进行处理
            return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
        } else if (!constructorMappings.isEmpty()) {
            // 如果指定了构造参数映射,使用构造参数映射来进行构造
            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, columnPrefix);
        }
        throw new ExecutorException("Do not know how to create an instance of " + resultType);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    通常我们的映射实体都是通过默认构造函数来进行创建的,也即 objectFactory.create(resultType) 方法逻辑。

    objectFactory.create 的创建逻辑就是通过反射进行创建,就不看源码了。

    1.2.2 自动映射

    org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getRowValue(org.apache.ibatis.executor.resultset.ResultSetWrapper, org.apache.ibatis.mapping.ResultMap)

    foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
    
    • 1

    org.apache.ibatis.executor.resultset.DefaultResultSetHandler#applyAutomaticMappings

    private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
        // 获取查询结果集中出现但是没有定义resultMapping的列
        // ResultSetWrapper会通过ResultSet来解析出当前查询结果返回的所有列
        // 从ResultMap中可以获取到当前已经指定了映射的列
        // 然后就可以得出有哪些查询结果中的列没有指定映射
        List<UnMappedColumnAutoMapping> autoMapping = 
            createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
        boolean foundValues = false;
        if (!autoMapping.isEmpty()) {
            // 遍历这些没有指定映射的结果集中的列
            for (UnMappedColumnAutoMapping mapping : autoMapping) {
                // 使用typeHandler进行转换,转换成Java类型
                final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
                if (value != null) {
                    foundValues = true;
                }
                if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
                    // 通过MetaObject将值设置到结果对象中
                    // gcode issue #377, call setter on nulls (value is not 'found')
                    metaObject.setValue(mapping.property, value);
                }
            }
        }
        return foundValues;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    该方法比较简单,先获取未配置 resultMap 映射节点的信息,即 List 对象,然后遍历该集合,获取属性值并设置到对象属性中,完成映射。

    那我们来看看如何获取没有定义 resultMap 映射信息的集合,即 createAutomaticMappings 方法。

    org.apache.ibatis.executor.resultset.DefaultResultSetHandler#createAutomaticMappings

    private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
        final String mapKey = resultMap.getId() + ":" + columnPrefix;
        // 从缓存中获取 UnMappedColumnAutoMapping 列表
        List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
    
        // 缓存未命中
        if (autoMapping == null) {
            autoMapping = new ArrayList<UnMappedColumnAutoMapping>();
    
            // 从 ResultSetWrapper 中获取未配置在  中的列名
            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)) {
                    // 检测当前属性是否存在于 resultMap 中
                    if (resultMap.getMappedProperties().contains(property)) {
                        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 {
                    configuration.getAutoMappingUnknownColumnBehavior()
                        .doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
                }
            }
            // 写入缓存
            autoMappingsCache.put(mapKey, autoMapping);
        }
        return autoMapping;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    原来该方法是从 ResultSetWrapper 中获取未配置 中的列名啊!然后处理属性的命名即类型,最后封装成 UnMappedColumnAutoMapping 对象。

    那我们来看看 getUnmappedColumnNames 方法源码。

    org.apache.ibatis.executor.resultset.ResultSetWrapper#getUnmappedColumnNames

    public List<String> getUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
        List<String> unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
        if (unMappedColumnNames == null) {
            // 加载已映射与未映射列名
            loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
            // 获取未映射列名
            unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
        }
        return unMappedColumnNames;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    org.apache.ibatis.executor.resultset.ResultSetWrapper#loadMappedAndUnmappedColumnNames

    private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
        List<String> mappedColumnNames = new ArrayList<String>();
        List<String> unmappedColumnNames = new ArrayList<String>();
        final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);
    
        // 获取  中配置的所有列名
        final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);
    
        /*
         * 遍历 columnNames,columnNames 是 ResultSetWrapper 的成员变量,保存了当前结果集中的所有列名
         * 这里是通过ResultSet中的所有列名来获取没有在resultMap中配置的列名
         * 意思是后面进行自动赋值时,只赋值查出来的列名
         */
        for (String columnName : columnNames) {
            final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);
            // 检测已映射列名集合中是否包含当前列名
            if (mappedColumns.contains(upperColumnName)) {
                mappedColumnNames.add(upperColumnName);
            } else {
                // 将列名存入 unmappedColumnNames 中
                unmappedColumnNames.add(columnName);
            }
        }
        // 缓存列名集合
        mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);
        unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    获取未配置 resultMap 的映射流程很简单,就是先获取 resultMap 配置的映射信息,然后循环需要映射结果集对象的所有属性,如果属性在 resultMap 中则表示配置了映射信息放入 mappedColumnNamesMap 中,反正放入 unMappedColumnNamesMap ,最后将 unMappedColumnNamesMap 返回就是我们要的未配置 resultMap 映射信息的集合了。

    1.2.3 resultMap 映射

    org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getRowValue(org.apache.ibatis.executor.resultset.ResultSetWrapper, org.apache.ibatis.mapping.ResultMap)

    foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
    
    • 1

    org.apache.ibatis.executor.resultset.DefaultResultSetHandler#applyPropertyMappings

    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();
        // 所有的ResultMapping遍历进行映射
        for (ResultMapping propertyMapping : propertyMappings) {
            String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
            if (propertyMapping.getNestedResultMapId() != null) {
                // the user added a column attribute to a nested result map, ignore it
                column = null;
            }
            if (propertyMapping.isCompositeResult()
                || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
                || propertyMapping.getResultSet() != null) {
                // 从结果集中获取指定列的数据
                Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
                // issue #541 make property optional
                final String property = propertyMapping.getProperty();
                if (property == null) {
                    continue;
                    // 若获取到的值为 DEFERED,则延迟加载该值
                } else if (value == DEFERED) {
                    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    该方法的处理就是遍历 resultMap 配置的映射信息,挨个获取其对应的值,该值可能是关联查询结果也可能是普通结果,最后将获取到的值设置到映射结果对象中。

    那接下来看看,这个 value 是如何获取的,方法源码。

    org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getPropertyMappingValue

    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);   // TODO is that OK?
            return DEFERED;
        } else {
            final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
            final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
            // 从 ResultSet 中获取指定列的值
            return typeHandler.getResult(rs, column);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这里和自动映射有一点不同,自动映射是从直接从ResultSet 中获取指定列的值,但是通过 ResultMap 多了一种情况,那就是关联查询,也可以说是延迟查询,此关联查询如果没有配置延迟加载,那么就要获取关联查询的值,如果配置了延迟加载,则返回DEFERED。

    至此,我们的结果集映射就已经分析完成了。

    好了,今天的内容到这里就结束了,我是 【J3】关注我,我们下期见


    • 由于博主才疏学浅,难免会有纰漏,假如你发现了错误或偏见的地方,还望留言给我指出来,我会对其加以修正。

    • 如果你觉得文章还不错,你的转发、分享、点赞、留言就是对我最大的鼓励。

    • 感谢您的阅读,十分欢迎并感谢您的关注。

  • 相关阅读:
    关于#django#的问题:django使用fdfs-client-py连接fastdfs遇到的问题
    汽车社媒营销创新玩法,品牌“自爆”不走寻常路
    vb圣经加注释
    Android简易音乐重构MVVM Java版-BottomNavigationView+viewpager主界面结构(十一)
    复现urlcode编码绕过xss限制两个demo
    YOLOv5 backbone
    Sandboxie+Buster Sandbox Analyzer打造个人沙箱
    股票魔法师第二阶段趋势模板选股公式,寻找上涨趋势
    游戏心理学Day20
    STM32使用PB3, PB4引脚的注意事项
  • 原文地址:https://blog.csdn.net/qq_40399646/article/details/127934982