MyBatis延迟加载使用及原理剖析

延迟加载:假设存在用户、订单两张表,可以查询用户(User)及用户对应的订单(Order)列表(一对多);用户信息作为主体,而订单信息不是立即需要获取到的情况下,MyBatis提供延迟加载的策略,发送SQL执行语句时,只查询用户信息,当需要使用到订单信息时,即user.getOrderList()时,才会发送获取订单信息的SQL查询订单信息 (需要用到对应的信息时,才执行相关SQL);

MyBatis延迟加载本质上:通过动态代理的形式,创建了目标对象(User)的代理对象,拦截了对象的getting方法,在执行getting方法时,进入拦截器的invoke方法,当发现需要延迟加载时,会把之前存放好的SQL语句进行执行,并调用对象(User)调用set方法存值,然后调用对象本身的get方法取值

本文主要讨论

1.如何实现MyBatis的延迟加载功能(MyBatis默认不开启延迟加载);

2.剖析源码查看器延迟加载原理
 

1.1  延迟加载开启(局部)

A.MyBatis中延迟加载的实现是,将复杂的SQL语句进行拆分分步执行从而到达延迟的目的

如下根据订单ID查询订单及对应用户 (建议:一对多,多对多通常采用延迟加载,一对一通常采用立即加载,此案例仅为了简单说明)的多表关联查询,分为两步

select * from orders o left join user u on o.uid = u.id where  o.id = 1 

根据i订单ID查询用户 

根据第一步查出来的订单对应的uid(即两表关联时的订单表的UID)作为ID查询用户表

SELECT * FROM orders O where id = 1

SELECT * FROM user where id = #{uid}

 B.增加两个对应的接口及sql编写

    <select id="findById" resultMap="orderUserResult" parameterType="int">
        SELECT * FROM orders O where id = #{id}
    </select>


    <select id="findById" parameterType="int" resultType="com.kay.pojo.User">
        select * from user where id = #{id}
    </select>

C.在orderMapper.xml中,在定义Order返回的结果集类型中<association>标签中 增加三个属性 fetchType="lazy" select="com.kay.dao.UserMapper.findById" column="uid"  fetchType 可以设置为lazy(懒加载)、eager(立即加载 默认此加载模式)select 为 order对象中涉及到的根据订单ID获取user对象对应的接口地址; colunm 为查询用户信息时,order表中需要传入的字段;( 属性可配置在<association> 或者 <collection>标签下)

    <resultMap id="orderUserResult" type="com.kay.pojo.Order">
        <result column="id" property="id"></result>
        <result column="ordertime" property="orderTime"></result>
        <result column="total" property="total"></result>

        <!-- 局部延迟加载 2  !!!!!!!! -->
        <!-- fetchType="lazy"  默认为eager  select 拆分成多条SQL语句后,在 SELECT * FROM orders O where id = #{id} 后执行的另一条对应的mapper类地址
       column="uid" 当前order 表是uid与user表中字段关联 -->
        <association property="user" javaType="com.kay.pojo.User" fetchType="lazy"
                     select="com.kay.dao.UserMapper.findById" column="uid">
            <result column="uid" property="id"></result>
            <result column="username" property="username"></result>
            <result column="password" property="password"></result>
            <result column="birthday" property="birthday"></result>
        </association>
    </resultMap>

D.验证 查询order及调用Order中getOrderTime不会执行查询用户信息的SQL,只有调用getUser后才发送了查询用户信息的SQL

1.2  延迟加载开启(全局)  当全局和局部同时配置了,以局部的为准

在核心配置文件中增加<setting> 配置 lazyLoadingEnabled为true

    <settings>
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>

2. 延迟加载源码剖析

在加载Mybatis核心配置文件的XMLConfigBuilder类中,向Configuration对象中赋值时,默认不开启延迟加载,触发一次延迟加载的方法包括 : equals,clone,hashCode,toString 

configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
 configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));

A. ResultSetHandler的唯一实现类DefaultResultSetHandler中的createResultObject()方法创建映射后的结果对象时,根据判断当前的ResultMap中开启了lazy,通过ProxyFactory创建ResultObject的代理对象 

configuration.getProxyFactory() 

protected ProxyFactory proxyFactory = new JavassistProxyFactory();

=== >>> 延迟加载默认有Javassist实现

B.JavassistProxyFactory中的createProxy() 实际是调用的 EnhancedResultObjectProxyImpl.createProxy() ;

在此方法中,创建EnhancedResultObjectProxyImpl callback实例,传入 crateProxy(type, callback, constructorArgTypes, constructorArgs);中,在后者中创建代理对象,并对其设置代理执行器callback。故在调用对象方法时,调用的是EnhancedResultObjectProxyImpl中的invoke方法

    @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);
    }

C.由invoke方法可见: 代理对象实例对应属性执行了set方法,此实例对象的对应属性将不再执行延迟加载;如果调用了 get 方法,则执行延迟加载

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值