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编写

    


    

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

    
        
        
        

        
        
        
            
            
            
            
        
    

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

MyBatis延迟加载使用及原理剖析_第1张图片

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

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

    
        
    

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的代理对象 

MyBatis延迟加载使用及原理剖析_第2张图片

configuration.getProxyFactory() 

protected ProxyFactory proxyFactory = new JavassistProxyFactory();

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

MyBatis延迟加载使用及原理剖析_第3张图片

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> constructorArgTypes, List constructorArgs) {
        return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
    } 
  

MyBatis延迟加载使用及原理剖析_第4张图片

MyBatis延迟加载使用及原理剖析_第5张图片

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

MyBatis延迟加载使用及原理剖析_第6张图片

你可能感兴趣的:(Mybatis系列,mybatis,java,动态代理)