当前位置:   article > 正文

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

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查询用户表

  1. SELECT * FROM orders O where id = 1
  2. SELECT * FROM user where id = #{uid}

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

  1. <select id="findById" resultMap="orderUserResult" parameterType="int">
  2. SELECT * FROM orders O where id = #{id}
  3. </select>
  4. <select id="findById" parameterType="int" resultType="com.kay.pojo.User">
  5. select * from user where id = #{id}
  6. </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>标签下)

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

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

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

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

  1. <settings>
  2. <setting name="lazyLoadingEnabled" value="true"/>
  3. </settings>

2. 延迟加载源码剖析

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

  1. configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
  2. 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方法

  1. @Override
  2. public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
  3. return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
  4. }

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/438022
推荐阅读
相关标签
  

闽ICP备14008679号