美文网首页mybatis
Mybatis源码分析——结果集ResultSet自动映射成实体

Mybatis源码分析——结果集ResultSet自动映射成实体

作者: 小波同学 | 来源:发表于2021-01-01 20:53 被阅读0次

    前言

    上一篇文章我们已经将SQL发送到了数据库,并返回了ResultSet,接下来就是将结果集 ResultSet 自动映射成实体类对象。这样使用者就无需再手动操作结果集,并将数据填充到实体类对象中。这可大大降低开发的工作量,提高工作效率。

    映射结果入口

    我们来看看上次看源码的位置

    public class PreparedStatementHandler extends BaseStatementHandler {
    
        @Override
        public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
            PreparedStatement ps = (PreparedStatement) statement;
            //执行数据库SQL
            ps.execute();
            //进行resultSet自动映射
            return resultSetHandler.<E> handleResultSets(ps);
        }
    }
    

    结果集的处理入口方法是 handleResultSets

    public class DefaultResultSetHandler implements ResultSetHandler {
    
        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 = resultMaps.get(resultSetCount);
                // 处理结果集
                handleResultSet(rsw, resultMap, multipleResults, null);
                //获取结果集的下一个结果
                rsw = getNextResultSet(stmt);
                cleanUpAfterHandlingResultSet();
                resultSetCount++;
            }
            
            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);
        }
    }
    

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

    public class DefaultResultSetHandler implements ResultSetHandler {
    
        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中
                        multipleResults.add(defaultResultHandler.getResultList());
                    } else {
                        handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
                    }
                }
            } finally {
                // issue #228 (close resultsets)
                closeResultSet(rsw.getResultSet());
            }
        }
    }
    

    通过handleRowValues 映射ResultSet结果,最后映射的结果会在defaultResultHandler的ResultList集合中,最后将结果加入到multipleResults中就可以返回了,我们继续跟进handleRowValues这个核心方法

    public class DefaultResultSetHandler implements ResultSetHandler {
    
        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);
            }
        }
    }
    

    我们可以通过resultMap.hasNestedResultMaps()知道查询语句是否是嵌套查询,如果resultMap中包含<association> 和 <collection>且其select属性不为空,则为嵌套查询。本文先分析简单的映射

    public class DefaultResultSetHandler implements ResultSetHandler {
    
        private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
          throws SQLException {
            DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
            // 根据 RowBounds 定位到指定行记录
            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());
            }
        }
    }
    

    我们查询的结果很有可能是一个集合,所以这里要遍历集合,每条结果单独进行映射,最后映射的结果加入到resultHandler的ResultList

    MyBatis 默认提供了 RowBounds 用于分页,从上面的代码中可以看出,这并非是一个高效的分页方式,是查出所有的数据,进行内存分页。除了使用 RowBounds,还可以使用一些第三方分页插件进行分页。我们后面文章来讲,我们来看关键代码getRowValue,处理一行数据

    public class DefaultResultSetHandler implements ResultSetHandler {
    
        private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
            // 这个Map是用来存储延迟加载的BountSql的,我们下面来看
            final ResultLoaderMap lazyLoader = new ResultLoaderMap();
            // 创建实体类对象,比如 User 对象
            Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
            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;
                }
                // 根据 <resultMap> 节点中配置的映射关系进行映射
                foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
                foundValues = lazyLoader.size() > 0 || foundValues;
                rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
            }
            return rowValue;
        }
    }
    

    重要的逻辑已经注释出来了。分别如下:

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

    创建实体类对象

    我们想将查询结果映射成实体类对象,第一步当然是要创建实体类对象了,下面我们来看一下 MyBatis 创建实体类对象的过程。

    public class DefaultResultSetHandler implements ResultSetHandler {
    
        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);
            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;
                    }
                }
            }
            this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
            return resultObject;
        }
    }
    

    我们先来看 createResultObject 重载方法的逻辑

    public class DefaultResultSetHandler implements ResultSetHandler {
    
        private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
          throws SQLException {
            final Class<?> resultType = resultMap.getType();
            final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
            final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
            if (hasTypeHandlerForResultObject(rsw, resultType)) {
                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);
        }
    }
    

    一般情况下,MyBatis 会通过 ObjectFactory 调用默认构造方法创建实体类对象。看看是如何创建的

    public class DefaultObjectFactory implements ObjectFactory, Serializable {
    
        @Override
        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();
                    if (!constructor.isAccessible()) {
                        constructor.setAccessible(true);
                    }
                    return constructor.newInstance();
                }
                //通过反射获取构造器
                constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
                if (!constructor.isAccessible()) {
                    constructor.setAccessible(true);
                }
                //通过构造器来实例化对象
                return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
            } catch (Exception e) {
                StringBuilder argTypes = new StringBuilder();
                if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
                    for (Class<?> argType : constructorArgTypes) {
                        argTypes.append(argType.getSimpleName());
                        argTypes.append(",");
                    }
                    argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
                }
                StringBuilder argValues = new StringBuilder();
                if (constructorArgs != null && !constructorArgs.isEmpty()) {
                    for (Object argValue : constructorArgs) {
                        argValues.append(String.valueOf(argValue));
                        argValues.append(",");
                    }
                    argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
                }
                throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
            }
        }
    }
    

    很简单,就是通过反射创建对象

    结果集映射

    映射结果集分为两种情况:

    • 1、是自动映射(结果集有但在resultMap里没有配置的字段),在实际应用中,都会使用自动映射,减少配置的工作。自动映射在Mybatis中也是默认开启的。
    • 2、是映射ResultMap中配置的。

    我们分这两者映射来看

    自动映射

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

    public class DefaultResultSetHandler implements ResultSetHandler {
    
        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) {
                    // 通过 TypeHandler 从结果集中获取指定列的数据
                    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')
                        // 通过元信息对象设置 value 到实体类对象的指定字段上
                        metaObject.setValue(mapping.property, value);
                    }
                }
            }
            return foundValues;
        }
    }
    

    首先是获取 UnMappedColumnAutoMapping 集合,然后遍历该集合,并通过 TypeHandler 从结果集中获取数据,最后再将获取到的数据设置到实体类对象中。

    UnMappedColumnAutoMapping 用于记录未配置在 <resultMap> 节点中的映射关系。它的代码如下:

    private static class UnMappedColumnAutoMapping {
        private final String column;
        private final String property;
        private final TypeHandler<?> typeHandler;
        private final boolean primitive;
    
        public UnMappedColumnAutoMapping(String column, String property, TypeHandler<?> typeHandler, boolean primitive) {
            this.column = column;
            this.property = property;
            this.typeHandler = typeHandler;
            this.primitive = primitive;
        }
    }
    

    仅用于记录映射关系。下面看一下获取 UnMappedColumnAutoMapping 集合的过程,如下:

    public class DefaultResultSetHandler implements ResultSetHandler {
    
        private final Configuration configuration;
        private final MappedStatement mappedStatement;
        
        private final TypeHandlerRegistry typeHandlerRegistry;
    
        private final Map<String, List<UnMappedColumnAutoMapping>> autoMappingsCache = new HashMap<String, List<UnMappedColumnAutoMapping>>();
    
        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 中获取未配置在 <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)) {
                        // 检测当前属性是否存在于 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;
        }
    }
    

    先来看看从 ResultSetWrapper 中获取未配置在 <resultMap> 中的列名
    即跟进

    public class ResultSetWrapper {
    
        private final Map<String, List<String>> unMappedColumnNamesMap = new HashMap<String, List<String>>();
    
        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<String>();
            List<String> unmappedColumnNames = new ArrayList<String>();
            final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);
            
            // 获取 <resultMap> 中配置的所有列名
            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);
        }
    }
    

    首先是从当前数据集中获取列名集合,然后获取 <resultMap> 中配置的列名集合。之后遍历数据集中的列名集合,并判断列名是否被配置在了 <resultMap> 节点中。若配置了,则表明该列名已有映射关系,此时该列名存入 mappedColumnNames 中。若未配置,则表明列名未与实体类的某个字段形成映射关系,此时该列名存入 unmappedColumnNames 中。

    映射result节点

    foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
    接下来分析一下 MyBatis 是如何将结果集中的数据填充到已配置ResultMap映射的实体类字段中的。

    public class DefaultResultSetHandler implements ResultSetHandler {
    
        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;
        }
        
        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);
            }
        }
    }
    

    从 ResultMap 获取映射对象 ResultMapping 集合。然后遍历 ResultMapping 集合,再此过程中调用 getPropertyMappingValue 获取指定指定列的数据,最后将获取到的数据设置到实体类对象中。

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

    关联查询与延迟加载

    我们的查询经常会碰到一对一,一对多的情况,通常我们可以用一条 SQL 进行多表查询完成任务。当然我们也可以使用关联查询,将一条 SQL 拆成两条去完成查询任务。MyBatis 提供了两个标签用于支持一对一和一对多的使用场景,分别是 <association> 和 <collection>。下面我来演示一下如何使用 <association> 完成一对一的关联查询。先来看看实体类的定义:

    /** 作者类 */
    @Data
    public class Author {
        private Integer id;
        private String name;
        private Integer age;
        private Integer sex;
        private String email;
    }
    
    /** 文章类 */
    @Data
    public class Article {
        private Integer id;
        private String title;
        private String content;
        private Date createTime;
        
        // 一对一关系
        private Author author;
    }
    

    接下来看一下 Mapper 接口与映射文件的定义。

    public interface ArticleMapper {
        Article findOne(@Param("id") int id);
        Author findAuthor(@Param("id") int authorId);
    }
    
    <mapper namespace="com.yibo.mapper.ArticleMapper">
        <resultMap id="articleResult" type="Article">
            <result property="createTime" column="create_time"/>
            //column 属性值仅包含列信息,参数类型为 author_id 列对应的类型,这里为 Integer
            //意思是将author_id做为参数传给关联的查询语句findAuthor
            <association property="author" column="author_id" javaType="Author" select="findAuthor"/>
        </resultMap>
    
        <select id="findOne" resultMap="articleResult">
            SELECT
                id, author_id, title, content, create_time
            FROM
                article
            WHERE
                id = #{id}
        </select>
    
        <select id="findAuthor" resultType="Author">
            SELECT
                id, name, age, sex, email
            FROM
                author
            WHERE
                id = #{id}
        </select>
    </mapper>
    

    开启延迟加载

    <!-- 开启延迟加载 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!-- 关闭积极的加载策略 -->
    <setting name="aggressiveLazyLoading" value="false"/>
    <!-- 延迟加载的触发方法 -->
    <setting name="lazyLoadTriggerMethods" value="equals,hashCode"/>
    

    此时association节点使用了select指向另外一个查询语句,并且将 author_id作为参数传给关联查询的语句

    此时如果不开启延迟加载,那么会生成两条SQL,先执行findOne,然后通过findOne的返回结果做为参数,执行findAuthor语句,并将结果设置到author属性

    如果开启了延迟加载呢?那么只会执行findOne一条SQL,当调用article.getAuthor()方法时,才会去执行findAuthor进行查询,我们下面来看看是如何实现的

    我们还是要从上面映射result节点说起

    public class DefaultResultSetHandler implements ResultSetHandler {
        
        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);
            }
        }
    }
    

    我们看到,如果ResultMapping设置了关联查询,也就是association或者collection配置了select,那么就要通过关联语句来查询结果,并设置到实体类对象的属性中了。如果没配置select,那就简单,直接从ResultSet中通过列名获取结果。那我们来看看getNestedQueryMappingValue

    public class DefaultResultSetHandler implements ResultSetHandler {
        
        private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
          throws SQLException {
            // 获取关联查询 id,id = 命名空间 + <association> 的 select 属性值
            final String nestedQueryId = propertyMapping.getNestedQueryId();
            final String property = propertyMapping.getProperty();
            // 根据 nestedQueryId 获取关联的 MappedStatement
            final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
            //获取关联查询MappedStatement的参数类型
            final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
            /*
             * 生成关联查询语句参数对象,参数类型可能是一些包装类,Map 或是自定义的实体类,
             * 具体类型取决于配置信息。以上面的例子为基础,下面分析不同配置对参数类型的影响:
             *   1. <association column="author_id"> 
             *      column 属性值仅包含列信息,参数类型为 author_id 列对应的类型,这里为 Integer
             * 
             *   2. <association column="{id=author_id, name=title}"> 
             *      column 属性值包含了属性名与列名的复合信息,MyBatis 会根据列名从 ResultSet 中
             *      获取列数据,并将列数据设置到实体类对象的指定属性中,比如:
             *          Author{id=1, name="陈浩"}
             *      或是以键值对 <属性, 列数据> 的形式,将两者存入 Map 中。比如:
             *          {"id": 1, "name": "陈浩"}
             *
             *      至于参数类型到底为实体类还是 Map,取决于关联查询语句的配置信息。比如:
             *          <select id="findAuthor">  ->  参数类型为 Map
             *          <select id="findAuthor" parameterType="Author"> -> 参数类型为实体类
             */
            final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, propertyMapping, nestedQueryParameterType, columnPrefix);
            Object value = null;
            if (nestedQueryParameterObject != null) {
                // 获取 BoundSql,这里设置了运行时参数,所以这里是能直接执行的
                final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
                final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
                final Class<?> targetType = propertyMapping.getJavaType();
                if (executor.isCached(nestedQuery, key)) {
                    executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
                    value = DEFERED;
                } else {
                    // 创建结果加载器
                    final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
                    // 检测当前属性是否需要延迟加载
                    if (propertyMapping.isLazy()) {
                        // 添加延迟加载相关的对象到 loaderMap 集合中
                        lazyLoader.addLoader(property, metaResultObject, resultLoader);
                        value = DEFERED;
                    } else {
                        // 直接执行关联查询
                        // 如果只是配置关联查询,但是没有开启懒加载,则直接执行关联查询,并返回结果,设置到实体类对象的属性中
                        value = resultLoader.loadResult();
                    }
                }
            }
            return value;
        }
    }
    

    下面先来总结一下该方法的逻辑:

    • 1、根据 nestedQueryId 获取 MappedStatement
    • 2、生成参数对象
    • 3、获取 BoundSql
    • 4、创建结果加载器 ResultLoader
    • 5、检测当前属性是否需要进行延迟加载,若需要,则添加延迟加载相关的对象到loaderMap 集合中
    • 6、如不需要延迟加载,则直接通过结果加载器加载结果

    以上流程中针对一级缓存的检查是十分有必要的,若缓存命中,可直接取用结果,无需再在执行关联查询 SQL。若缓存未命中,接下来就要按部就班执行延迟加载相关逻辑

    我们来看一下添加延迟加载相关对象到 loaderMap 集合中的逻辑,如下:

    public class ResultLoaderMap {
    
        private final Map<String, LoadPair> loaderMap = new HashMap<String, LoadPair>();
    
        public void addLoader(String property, MetaObject metaResultObject, ResultLoader resultLoader) {
            // 将属性名转为大写
            String upperFirst = getUppercaseFirstProperty(property);
            if (!upperFirst.equalsIgnoreCase(property) && loaderMap.containsKey(upperFirst)) {
              throw new ExecutorException("Nested lazy loaded result property '" + property +
                      "' for query id '" + resultLoader.mappedStatement.getId() +
                      " already exists in the result map. The leftmost property of all lazy loaded properties must be unique within a result map.");
            }
            // 创建 LoadPair,并将 <大写属性名,LoadPair对象> 键值对添加到 loaderMap 中
            loaderMap.put(upperFirst, new LoadPair(property, metaResultObject, resultLoader));
        }
    }
    

    我们再来回顾一下文章开始的创建实体类

    public class DefaultResultSetHandler implements ResultSetHandler {
    
        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);
            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;
                    }
                }
            }
            this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
            return resultObject;
        }
    }
    

    如果开启了延迟加载,并且有关联查询,此时是要创建一个代理对象的,将上面存放BondSql的lazyLoader和创建的目标对象resultObject 作为参数传进去。

    Mybatis提供了两个实现类CglibProxyFactory和JavassistProxyFactory,分别基于org.javassist:javassist和cglib:cglib进行实现。createProxy方法就是实现懒加载逻辑的核心方法,也是我们分析的目标。

    CglibProxyFactory

    CglibProxyFactory基于cglib动态代理模式,通过继承父类的方式生成动态代理类。

    public class CglibProxyFactory implements ProxyFactory {
    
        @Override
        public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
            return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
        }
        
        public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
            final Class<?> type = target.getClass();
            EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
            //由CglibProxyFactory生成对象
            Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
            //复制属性
            PropertyCopier.copyBeanProperties(type, target, enhanced);
            return enhanced;
        }
        
        static Object crateProxy(Class<?> type, Callback callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
            Enhancer enhancer = new Enhancer();
            enhancer.setCallback(callback);
            //设置父类对象
            enhancer.setSuperclass(type);
            try {
                type.getDeclaredMethod(WRITE_REPLACE_METHOD);
                // ObjectOutputStream will call writeReplace of objects returned by writeReplace
                if (log.isDebugEnabled()) {
                    log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
                }
            } catch (NoSuchMethodException e) {
                enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class});
            } catch (SecurityException e) {
                // nothing to do here
            }
            Object enhanced;
            if (constructorArgTypes.isEmpty()) {
                enhanced = enhancer.create();
            } else {
                Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
                Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
                enhanced = enhancer.create(typesArray, valuesArray);
            }
            return enhanced;
        }
    }
    

    可以看到,初始化Enhancer,并调用构造方法,生成对象。从enhancer.setSuperclass(type);也能看出cglib采用的是继承父类的方式。

    EnhancedResultObjectProxyImpl

    EnhancedResultObjectProxyImpl实现了MethodInterceptor接口,此接口是Cglib拦截目标对象方法的入口,对目标对象方法的调用都会通过此接口的intercept的方法。

    public class CglibProxyFactory implements ProxyFactory {
    
        private static class EnhancedResultObjectProxyImpl implements MethodInterceptor {
        
            private final Class<?> type;
            private final ResultLoaderMap lazyLoader;
            private final boolean aggressive;
            private final Set<String> lazyLoadTriggerMethods;
            private final ObjectFactory objectFactory;
            private final List<Class<?>> constructorArgTypes;
            private final List<Object> constructorArgs;
    
            @Override
            public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                final String methodName = method.getName();
                try {
                    synchronized (lazyLoader) {
                        if (WRITE_REPLACE_METHOD.equals(methodName)) {
                            Object original;
                            if (constructorArgTypes.isEmpty()) {
                                original = objectFactory.create(type);
                            } else {
                                original = objectFactory.create(type, constructorArgTypes, constructorArgs);
                            }
                            PropertyCopier.copyBeanProperties(type, enhanced, original);
                            if (lazyLoader.size() > 0) {
                                return new CglibSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
                            } else {
                                return original;
                            }
                        } else {
                            if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
                                /*
                                 * 如果 aggressive 为 true,或触发方法(比如 equals,hashCode 等)被调用,
                                 * 则加载所有的所有延迟加载的数据
                                 */
                                if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
                                    lazyLoader.loadAll();
                                } else if (PropertyNamer.isSetter(methodName)) {
                                    // 如果使用者显示调用了 setter 方法,则将相应的延迟加载类从 loaderMap 中移除
                                    final String property = PropertyNamer.methodToProperty(methodName);
                                    lazyLoader.remove(property);
                                 // 检测使用者是否调用 getter 方法
                                } else if (PropertyNamer.isGetter(methodName)) {
                                    final String property = PropertyNamer.methodToProperty(methodName);
                                    if (lazyLoader.hasLoader(property)) {
                                        // 执行延迟加载逻辑
                                        lazyLoader.load(property);
                                    }
                                }
                            }
                        }
                    }
                    //执行原方法(即父类方法)
                    return methodProxy.invokeSuper(enhanced, args);
                } catch (Throwable t) {
                    throw ExceptionUtil.unwrapThrowable(t);
                }
            }
        }
    }
    

    完整的代码

    
    // cglib代理工厂类,实现延迟加载属性
    public class CglibProxyFactory implements ProxyFactory {
    
      private static final Log log = LogFactory.getLog(CglibProxyFactory.class);
      //finalize方法
      private static final String FINALIZE_METHOD = "finalize";
      //writeReplace方法
      private static final String WRITE_REPLACE_METHOD = "writeReplace";
    
      //加载Enhancer,这个是Cglib的入口
      public CglibProxyFactory() {
        try {
          Resources.classForName("net.sf.cglib.proxy.Enhancer");
        } catch (Throwable e) {
          throw new IllegalStateException("Cannot enable lazy loading because CGLIB is not available. Add CGLIB to your classpath.", e);
        }
      }
    
      /**
       * 创建代理对象
       * @param target 目标对象
       * @param lazyLoader 延迟加载器
       * @param configuration 配置类
       * @param objectFactory 对象工厂
       * @param constructorArgTypes 构造函数类型[]
       * @param constructorArgs  构造函数的值[]
       * @return
       */
      @Override
      public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
      }
    
      /**
       * 创建一个反序列化代理
       * @param target 目标
       * @param unloadedProperties
       * @param objectFactory 对象工厂
       * @param constructorArgTypes 构造函数类型数组
       * @param constructorArgs 构造函数值
       * @return
       */
      public Object createDeserializationProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        return EnhancedDeserializationProxyImpl.createProxy(target, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
      }
    
      @Override
      public void setProperties(Properties properties) {
          // Not Implemented
      }
    
      /**
       * 返回代理对象, 这个代理对象在调用任何方法都会调用本类的intercept方法
       * Enhancer 认为这个就是自定义类的工厂,比如这个类需要实现什么接口
       * @param type 目标类型
       * @param callback 结果对象代理实现类,当中有invoke回调方法
       * @param constructorArgTypes 构造函数类型数组
       * @param constructorArgs 构造函数对应字段的值数组
       * @return
       */
      static Object crateProxy(Class<?> type, Callback callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        // enhancer 配置调节代理对象的一些参数
        // 设置回调方法
        // 设置超类
        //判断当传入目标类型是否有writeReplace方法,没有则配置一个有writeReplace方法的接口(序列化写出)
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback(callback);
        enhancer.setSuperclass(type);
        try {
          type.getDeclaredMethod(WRITE_REPLACE_METHOD);
          // ObjectOutputStream will call writeReplace of objects returned by writeReplace
          if (log.isDebugEnabled()) {
            log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
          }
        } catch (NoSuchMethodException e) {
          //这个enhancer增加一个WriteReplaceInterface接口
          enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class});
        } catch (SecurityException e) {
          // nothing to do here
        }
        
        //根据构造函数创建一个对象
        //无参构造
        //有参构造  
        Object enhanced;
        if (constructorArgTypes.isEmpty()) {
          enhanced = enhancer.create();
        } else {
          Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
          Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
          enhanced = enhancer.create(typesArray, valuesArray);
        }
        return enhanced;
      }
    
      /**
       * 结果对象代理实现类,
       * 它实现方法拦截器的intercept方法
       */
      private static class EnhancedResultObjectProxyImpl implements MethodInterceptor {
    
        private final Class<?> type;
        private final ResultLoaderMap lazyLoader;
        private final boolean aggressive;
        private final Set<String> lazyLoadTriggerMethods;
        private final ObjectFactory objectFactory;
        private final List<Class<?>> constructorArgTypes;
        private final List<Object> constructorArgs;
    
        /**
         * 代理对象创建
         * @param type 目标class类型
         * @param lazyLoader 延迟加载器
         * @param configuration 配置信息
         * @param objectFactory 对象工厂
         * @param constructorArgTypes 构造函数类型数组
         * @param constructorArgs 构造函数值数组
         */
        private EnhancedResultObjectProxyImpl(Class<?> type, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          this.type = type;
          this.lazyLoader = lazyLoader;
          this.aggressive = configuration.isAggressiveLazyLoading();
          this.lazyLoadTriggerMethods = configuration.getLazyLoadTriggerMethods();
          this.objectFactory = objectFactory;
          this.constructorArgTypes = constructorArgTypes;
          this.constructorArgs = constructorArgs;
        }
    
        /**
         * 创建代理对象, 将源对象值赋值给代理对象
         * @param target 目标对象
         * @param lazyLoader 延迟加载器
         * @param configuration 配置对象
         * @param objectFactory 对象工厂
         * @param constructorArgTypes 构造函数类型数组
         * @param constructorArgs 构造函数值数组
         * @return
         */
        public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          //获取目标的类型
          //创建一个结果对象代理实现类(它实现cglib的MethodInterface接口,完成回调作用invoke方法)      
          final Class<?> type = target.getClass();
          EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
          Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
          PropertyCopier.copyBeanProperties(type, target, enhanced);
          return enhanced;
        }
    
        /**
         * 回调方法
         * @param enhanced 代理对象
         * @param method 方法
         * @param args 方法参数
         * @param methodProxy 代理方法
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
          //获取方法名
          final String methodName = method.getName();
          try {
            // 同步获取延迟加载对象
            // 如果是执行writeReplace方法(序列化写出)
            // 实例化一个目标对象的实例   
            synchronized (lazyLoader) {
              if (WRITE_REPLACE_METHOD.equals(methodName)) {
                Object original;
                if (constructorArgTypes.isEmpty()) {
                  original = objectFactory.create(type);
                } else {
                  original = objectFactory.create(type, constructorArgTypes, constructorArgs);
                }
                // 将enhanced中的属性复制到orignal对象中
                // 如果延迟加载数量>0,          
                PropertyCopier.copyBeanProperties(type, enhanced, original);
                if (lazyLoader.size() > 0) {
                  return new CglibSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
                } else {
                  return original;
                }
              } else {
                //不是writeReplace方法
                // 延迟加载长度大于0, 且不是finalize方法
                // configuration配置延迟加载参数,延迟加载触发的方法包含这个方法
                // 延迟加载所有数据       
                if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
                  if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
                    lazyLoader.loadAll();
                  // setter方法,直接移除  
                  } else if (PropertyNamer.isSetter(methodName)) {
                    final String property = PropertyNamer.methodToProperty(methodName);
                    lazyLoader.remove(property);
                    
                  // getter方法, 加载该属性    
                  } else if (PropertyNamer.isGetter(methodName)) {
                    final String property = PropertyNamer.methodToProperty(methodName);
                    if (lazyLoader.hasLoader(property)) {
                      lazyLoader.load(property);
                    }
                  }
                }
              }
            }
            return methodProxy.invokeSuper(enhanced, args);
          } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
          }
        }
      }
    
      /**
       * 他继承抽象反序列化代理和实现了方法拦截
       */
      private static class EnhancedDeserializationProxyImpl extends AbstractEnhancedDeserializationProxy implements MethodInterceptor {
    
        private EnhancedDeserializationProxyImpl(Class<?> type, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
                List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          super(type, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
        }
    
        /**
         * 创建代理对象
         * @param target
         * @param unloadedProperties
         * @param objectFactory
         * @param constructorArgTypes
         * @param constructorArgs
         * @return
         */
        public static Object createProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
                List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          final Class<?> type = target.getClass();
          EnhancedDeserializationProxyImpl callback = new EnhancedDeserializationProxyImpl(type, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
          Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
          PropertyCopier.copyBeanProperties(type, target, enhanced);
          return enhanced;
        }
    
        @Override
        public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
          final Object o = super.invoke(enhanced, method, args);
          return o instanceof AbstractSerialStateHolder ? o : methodProxy.invokeSuper(o, args);
        }
    
        @Override
        protected AbstractSerialStateHolder newSerialStateHolder(Object userBean, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
                List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          return new CglibSerialStateHolder(userBean, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
        }
      }
    }
    

    如上,代理方法首先会检查 aggressive 是否为 true,如果不满足,再去检查 lazyLoadTriggerMethods 是否包含当前方法名。这里两个条件只要一个为 true,当前实体类中所有需要延迟加载。aggressive 和 lazyLoadTriggerMethods 两个变量的值取决于下面的配置。

    <setting name="aggressiveLazyLoading" value="false"/>
    <setting name="lazyLoadTriggerMethods" value="equals,hashCode"/>
    

    然后代理逻辑会检查使用者是不是调用了实体类的 setter 方法,如果调用了,就将该属性对应的 LoadPair 从 loaderMap 中移除。为什么要这么做呢?答案是:使用者既然手动调用 setter 方法,说明使用者想自定义某个属性的值。此时,延迟加载逻辑不应该再修改该属性的值,所以这里从 loaderMap 中移除属性对于的 LoadPair。

    最后如果使用者调用的是某个属性的 getter 方法,且该属性配置了延迟加载,此时延迟加载逻辑就会被触发。那接下来,我们来看看延迟加载逻辑是怎样实现的的。

    public class ResultLoaderMap {
    
        private final Map<String, LoadPair> loaderMap = new HashMap<String, LoadPair>();
      
        public boolean load(String property) throws SQLException {
            // 从 loaderMap 中移除 property 所对应的 LoadPair
            LoadPair pair = loaderMap.remove(property.toUpperCase(Locale.ENGLISH));
            if (pair != null) {
                // 加载结果
                pair.load();
                return true;
            }
            return false;
        }
        
        public void load() throws SQLException {
          /* These field should not be null unless the loadpair was serialized.
           * Yet in that case this method should not be called. */
          if (this.metaResultObject == null) {
            throw new IllegalArgumentException("metaResultObject is null");
          }
          if (this.resultLoader == null) {
            throw new IllegalArgumentException("resultLoader is null");
          }
    
          this.load(null);
        }
        
        public void load(final Object userObject) throws SQLException {
          if (this.metaResultObject == null || this.resultLoader == null) {
            if (this.mappedParameter == null) {
              throw new ExecutorException("Property [" + this.property + "] cannot be loaded because "
                      + "required parameter of mapped statement ["
                      + this.mappedStatement + "] is not serializable.");
            }
    
            final Configuration config = this.getConfiguration();
            final MappedStatement ms = config.getMappedStatement(this.mappedStatement);
            if (ms == null) {
              throw new ExecutorException("Cannot lazy load property [" + this.property
                      + "] of deserialized object [" + userObject.getClass()
                      + "] because configuration does not contain statement ["
                      + this.mappedStatement + "]");
            }
    
            this.metaResultObject = config.newMetaObject(userObject);
            this.resultLoader = new ResultLoader(config, new ClosedExecutor(), ms, this.mappedParameter,
                    metaResultObject.getSetterType(this.property), null, null);
          }
    
          /* We are using a new executor because we may be (and likely are) on a new thread
           * and executors aren't thread safe. (Is this sufficient?)
           *
           * A better approach would be making executors thread safe. */
          if (this.serializationCheck == null) {
            final ResultLoader old = this.resultLoader;
            this.resultLoader = new ResultLoader(old.configuration, new ClosedExecutor(), old.mappedStatement,
                    old.parameterObject, old.targetType, old.cacheKey, old.boundSql);
          }
    
        /*
         * 调用 ResultLoader 的 loadResult 方法加载结果,
         * 并通过 metaResultObject 设置结果到实体类对象中
         */
          this.metaResultObject.setValue(property, this.resultLoader.loadResult());
        }
    }
    
    public class ResultLoader {
    
        protected final ResultExtractor resultExtractor;
    
        protected Object resultObject;
    
        public Object loadResult() throws SQLException {
            // 执行关联查询
            List<Object> list = selectList();
            // 抽取结果
            resultObject = resultExtractor.extractObjectFromList(list, targetType);
            return resultObject;
        }
        
        private <E> List<E> selectList() throws SQLException {
            Executor localExecutor = executor;
            if (Thread.currentThread().getId() != this.creatorThreadId || localExecutor.isClosed()) {
                localExecutor = newExecutor();
            }
            try {
                // 通过 Executor 就行查询,这个之前已经分析过了
                // 这里的parameterObject和boundSql就是我们之前存放在LoadPair中的,现在直接拿来执行了
                return localExecutor.<E> query(mappedStatement, parameterObject, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, cacheKey, boundSql);
            } finally {
                if (localExecutor != executor) {
                    localExecutor.close(false);
                }
            }
        }
    }
    

    好了,延迟加载我们基本已经讲清楚了,我们介绍一下另外的一种代理方式

    JavassistProxyFactory

    JavassistProxyFactory使用的是javassist方式,直接修改class文件的字节码格式。

    /**JavassistProxy字节码生成代理
     * 1.创建一个代理对象然后将目标对象的值赋值给代理对象,这个代理对象是可以实现其他的接口
     * 2. JavassistProxyFactory实现ProxyFactory接口createProxy(创建代理对象的方法)
     * @author Eduardo Macarron
     */
    public class JavassistProxyFactory implements org.apache.ibatis.executor.loader.ProxyFactory {
    
      private static final Log log = LogFactory.getLog(JavassistProxyFactory.class);
      
      /**
       * finalize方法(垃圾回收)
       */
      private static final String FINALIZE_METHOD = "finalize";
      /**
       * writeReplace(序列化写出方法)
       */  
      private static final String WRITE_REPLACE_METHOD = "writeReplace";
    
      /**
       * 加载ProxyFactory, 也就是JavassistProxy的入口
       */
      public JavassistProxyFactory() {
        try {
          Resources.classForName("javassist.util.proxy.ProxyFactory");
        } catch (Throwable e) {
          throw new IllegalStateException("Cannot enable lazy loading because Javassist is not available. Add Javassist to your classpath.", e);
        }
      }
    
      /**
       * 创建代理
       * @param target 目标对象
       * @param lazyLoader 延迟加载Map集合(那些属性是需要延迟加载的)
       * @param configuration 配置类
       * @param objectFactory 对象工厂
       * @param constructorArgTypes 构造函数类型[]
       * @param constructorArgs  构造函数的值[]
       * @return
       */
      @Override
      public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
      }
    
      public Object createDeserializationProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        return EnhancedDeserializationProxyImpl.createProxy(target, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
      }
    
      @Override
      public void setProperties(Properties properties) {
          // Not Implemented
      }
    
      /**
       * 获取代理对象, 也就是说在执行方法之前首先调用MethodHanlder的invoke方法
       * @param type 目标类型
       * @param callback 回调对象
       * @param constructorArgTypes 构造函数类型数组
       * @param constructorArgs 构造函数值的数组
       * @return
       */
      static Object crateProxy(Class<?> type, MethodHandler callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        // 创建一个代理工厂类
        // 配置超类
        ProxyFactory enhancer = new ProxyFactory();
        enhancer.setSuperclass(type);
        //判断是否有writeReplace方法,如果没有将这个代理对象实现WriteReplaceInterface接口,这个接口只有一个writeReplace方法
        try {
          type.getDeclaredMethod(WRITE_REPLACE_METHOD);
          // ObjectOutputStream will call writeReplace of objects returned by writeReplace
          if (log.isDebugEnabled()) {
            log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
          }
        } catch (NoSuchMethodException e) {
          enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class});
        } catch (SecurityException e) {
          // nothing to do here
        }
    
        Object enhanced;
        Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
        Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
        try {
          // 根据构造函数创建一个代理对象
          enhanced = enhancer.create(typesArray, valuesArray);
        } catch (Exception e) {
          throw new ExecutorException("Error creating lazy proxy.  Cause: " + e, e);
        }
        // 设置回调对象
        ((Proxy) enhanced).setHandler(callback);
        return enhanced;
      }
    
      /**
       * 实现Javassist的MethodHandler接口, 相对于Cglib的MethodInterceptor
       * 他们接口的方法名也是不一样的,Javassist的是invoke, 而cglib是intercept,叫法不同,实现功能是一样的
       */
      private static class EnhancedResultObjectProxyImpl implements MethodHandler {
        /**
         * 目标类型
         */
        private final Class<?> type;
        /**
         * 延迟加载Map集合
         */ 
        private final ResultLoaderMap lazyLoader;
        /**
         * 是否配置延迟加载
         */ 
        private final boolean aggressive;
        /**
         * 延迟加载触发的方法
         */ 
        private final Set<String> lazyLoadTriggerMethods;
        /**
         * 对象工厂
         */ 
        private final ObjectFactory objectFactory;
        /**
         * 构造函数类型数组
         */ 
        private final List<Class<?>> constructorArgTypes;
        /**
         * 构造函数类型的值数组
         */ 
        private final List<Object> constructorArgs;
    
        /**
         * 构造函数私有化了
         * @param type
         * @param lazyLoader
         * @param configuration
         * @param objectFactory
         * @param constructorArgTypes
         * @param constructorArgs
         */
        private EnhancedResultObjectProxyImpl(Class<?> type, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          this.type = type;
          this.lazyLoader = lazyLoader;
          this.aggressive = configuration.isAggressiveLazyLoading();
          this.lazyLoadTriggerMethods = configuration.getLazyLoadTriggerMethods();
          this.objectFactory = objectFactory;
          this.constructorArgTypes = constructorArgTypes;
          this.constructorArgs = constructorArgs;
        }
    
        public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          // 获取目标类型
          // 创建一个EnhancedResultObjectProxyImpl对象,回调对象
          final Class<?> type = target.getClass();
          EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
          Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
          PropertyCopier.copyBeanProperties(type, target, enhanced);
          return enhanced;
        }
    
        /**
         * 回调方法
         * @param enhanced 代理对象
         * @param method 方法
         * @param methodProxy 代理方法
         * @param args 入参
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
          //获取方法名称  
          final String methodName = method.getName();
          try {
            synchronized (lazyLoader) {
              if (WRITE_REPLACE_METHOD.equals(methodName)) {
              //如果方法是writeReplace
                Object original;
                if (constructorArgTypes.isEmpty()) {
                  original = objectFactory.create(type);
                } else {
                  original = objectFactory.create(type, constructorArgTypes, constructorArgs);
                }
                PropertyCopier.copyBeanProperties(type, enhanced, original);
                if (lazyLoader.size() > 0) {
                  return new JavassistSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
                } else {
                  return original;
                }
              } else {
                //不是writeReplace方法
                // 延迟加载长度大于0, 且不是finalize方法
                // configuration配置延迟加载参数,延迟加载触发的方法包含这个方法
                // 延迟加载所有数据       
                if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
                  if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
                    lazyLoader.loadAll();
                  } else if (PropertyNamer.isSetter(methodName)) {
                    final String property = PropertyNamer.methodToProperty(methodName);
                    lazyLoader.remove(property);
                  } else if (PropertyNamer.isGetter(methodName)) {
                    final String property = PropertyNamer.methodToProperty(methodName);
                    if (lazyLoader.hasLoader(property)) {
                      lazyLoader.load(property);
                    }
                  }
                }
              }
            }
            return methodProxy.invoke(enhanced, args);
          } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
          }
        }
      }
    
      private static class EnhancedDeserializationProxyImpl extends AbstractEnhancedDeserializationProxy implements MethodHandler {
    
        private EnhancedDeserializationProxyImpl(Class<?> type, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
                List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          super(type, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
        }
    
        public static Object createProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
                List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          final Class<?> type = target.getClass();
          EnhancedDeserializationProxyImpl callback = new EnhancedDeserializationProxyImpl(type, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
          Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
          PropertyCopier.copyBeanProperties(type, target, enhanced);
          return enhanced;
        }
    
        @Override
        public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
          final Object o = super.invoke(enhanced, method, args);
          return o instanceof AbstractSerialStateHolder ? o : methodProxy.invoke(o, args);
        }
    
        @Override
        protected AbstractSerialStateHolder newSerialStateHolder(Object userBean, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
                List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          return new JavassistSerialStateHolder(userBean, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
        }
      }
    }
    

    参考:
    https://www.cnblogs.com/java-chen-hao/p/11760777.html

    相关文章

      网友评论

        本文标题:Mybatis源码分析——结果集ResultSet自动映射成实体

        本文链接:https://www.haomeiwen.com/subject/qpbjoktx.html