Bol5261 2021-09-22 19:28 采纳率: 97.3%
浏览 84
已结题

Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?

Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询。在 Mybatis配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled=true|false。它的原理是,使用 CGLIB 创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用 a.getB().getName(),拦截器 invoke()方法发现 a.getB()是null 值,那么就会单独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来,然后调用 a.setB(b),于是 a 的对象 b 属性就有值了,接着完成 a.getB().getName()方法的调用。这就是延迟加载的基本原理。当然了,不光是 Mybatis,几乎所有的包括 Hibernate,支持延迟加载的原理都是一样的。

  • 写回答

1条回答 默认 最新

  • 大鹏cool Java领域优质创作者 2021-09-23 09:24
    关注

    题主你好,关于 MyBatis 创建代理对象用于延迟加载的实现,默认情况下并非使用 cglib 实现,而是使用 javassist,如果需要调整为 cglib 可以在配置中的 proxyFactory 进行设置,可以指定为 CGLIB 或 JAVASSIST,官网相关配置说明见 mybatis 配置

    另外从源码中也可以验证官网的说明。源码中用于创建代理对象的代码如下:

    public class DefaultResultSetHandler implements ResultSetHandler {
        /**
         * 创建返回结果
         *
         * @param rsw          结果集
         * @param resultMap    结果映射
         * @param lazyLoader   结果的属性加载器 Map
         * @param columnPrefix 列前缀
         * @return
         * @throws SQLException
         */
        private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
            this.useConstructorMappings = false; // reset previous mapping result
            final List<Class<?>> constructorArgTypes = new ArrayList<>();
            final List<Object> constructorArgs = new ArrayList<>();
            Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
            if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
                final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
                for (ResultMapping propertyMapping : propertyMappings) {
                    // issue gcode #109 && issue #149
                    if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
                        // 创建懒加载的代理,用于懒加载属性
                        resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
                        break;
                    }
                }
            }
            this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
            return resultObject;
        }
    }
    

    这里的代理工厂 ProxyFactory 从配置 Configuration 中读取,Configuration 涉及 ProxyFactory 的代码如下。

    public class Configuration {
    
        protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
    
        public ProxyFactory getProxyFactory() {
            return proxyFactory;
        }
    
        public void setProxyFactory(ProxyFactory proxyFactory) {
            if (proxyFactory == null) {
                proxyFactory = new JavassistProxyFactory();
            }
            this.proxyFactory = proxyFactory;
        }
    }
    

    从配置 Configuration 中可以看出,默认的 ProxyFactory 是 JavassistProxyFactory。

    如果需要调整,对于纯 mybatis 项目,可以在 xml 配置文件中指定。

    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <properties>
            <!--<property name="proxyFactory" value="JAVASSIST"/>-->
            <property name="proxyFactory" value="CGLIB"/>
        </properties>
    </configuration>
    

    mybatis 解析 proxyFactory 配置的代码如下。

    public class XMLConfigBuilder extends BaseBuilder {
    
        private void settingsElement(Properties props) {
            ... 省略部分代码
            configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
            ... 省略部分代码
        }
    }
    

    对于技术的探索,最好还是持有怀疑态度,不要人云亦云。如果题主对自问自答这种形式感兴趣不妨写几篇博客。了解 mybatis 其他内容,还可以参阅我写的几篇关于 mybatis 的文章,专栏为 MyBatis 源码之旅

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 系统已结题 10月2日
  • 已采纳回答 9月24日
  • 创建了问题 9月22日

悬赏问题

  • ¥170 如图所示配置eNSP
  • ¥20 docker里部署springboot项目,访问不到扬声器
  • ¥15 netty整合springboot之后自动重连失效
  • ¥15 悬赏!微信开发者工具报错,求帮改
  • ¥20 wireshark抓不到vlan
  • ¥20 关于#stm32#的问题:需要指导自动酸碱滴定仪的原理图程序代码及仿真
  • ¥20 设计一款异域新娘的视频相亲软件需要哪些技术支持
  • ¥15 stata安慰剂检验作图但是真实值不出现在图上
  • ¥15 c程序不知道为什么得不到结果
  • ¥15 键盘指令混乱情况下的启动盘系统重装