MyBatis-延迟加载
...大约 4 分钟
MyBatis-延迟加载
学习核心
- MyBatis的延迟加载概念
学习资料
延迟加载概念
MyBatis的延迟加载
MyBatis支持延迟加载,但是默认没有开启。
延迟加载:需要用到数据的时候才进行加载,如果不需要用到数据则不加载
- MyBatis支持一对一、一对多关联的延迟加载
- 在MyBatis核心配置文件中可设定延迟加载开关(lazyLoadingEnabled)
场景案例
例如一个用户对用多个订单,此时可以构建用户-订单(1:n)的关系,在查询的时候有如下情况
立即加载:查询用户的时候,将用户所属的订单数据页查询出来
延迟加载:查询用户的时候,暂时不查询订单数据,但需要订单的时候再去查询订单(按需加载)
配置懒加载的方式有两种,一种是全局配置(在核心配置文件中开启懒加载),一种是局部配置(在对应的映射配置中开启懒加载)
- 全局配置(在核心配置文件中开启延迟加载开关)
<!-- 配置settings属性实现延迟缓存 -->
<settings>
<!-- 打开延迟加载的开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 将积极加载改为消极加载和按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
- 局部配置(参考)
在要指定懒加载的映射中配置fetchType="lazy"
(例如此处是查询用户关联的订单详情时开启懒加载,则在对应resultMap的collection配置中指定参数)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.noob.mybatis.c_many_to_many.UserDetailMapper">
<!-- 定义通过ResultMap实现多对多查询-->
<resultMap type="User" id="UserDetailResultMap">
<!-- 配置User基本属性 -->
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="gender" property="gender"/>
<result column="addr" property="addr"/>
<!-- 配置User对应订单信息(通过select封装) -->
<collection property="orderList" ofType="Orders"
select="com.noob.framework.mapper.OrderMapper.findByUid" column="id"
fetchType="lazy">
</collection>
</resultMap>
<select id="selectDetailById" resultMap="UserDetailResultMap">
select * from tb_user where id = #{id}
</select>
</mapper>
# com.noob.framework.mapper.OrderMapper.findByUid
<select id="findByUid" resultType="Order">
select * from tb_order where user_id = #{id}
</select>
测试
// 模拟执行SQL完成查询操作
User user = userMapper.selectDetailById(); // 语句1
System.out.println(user.getUsername()); // 语句2
// 测试延迟加载
List<Order> orderList = user.getOrderList(); // 语句3
System.out.println(orderList);
直接加载:当执行上述语句时,执行语句1会调用SQL语句查询数据并封装(此时用户信息和关联的订单信息都会关联查找出来)
延迟加载:当执行上述语句时,执行语句1时只会封装用户基本信息(只执行selectDetailById中定义的语句),此时orderList为null;只有到执行语句3需要用到orderList属性的时候才会真正调用com.noob.framework.mapper.OrderMapper.findByUid
完成订单信息查找操作并封装(通过set方法进行封装)
参考演示效果如上所示,以user.getUsername()分隔,可以看到当开启了延迟加载时,只有当要使用到orderList,MyBatis才会去执行真正获取订单信息的SQL语句后进行封装。
延迟加载原理
延迟加载原理(底层原理)
- 创建代理对象:使用CGLIB创建目标对象的代理对象
- 当调用目标方法时,进入拦截器invoke方法,发现通过getter获取到的对象是null值,就会执行相应的sql查询数据
- 获取到查询数据之后,调用set方法设置属性值,再继续完成调用
以上述案例进行分析
- 创建代理对象:使用CGLIB创建目标对象的代理对象
- 当调用目标方法
user.getOrderlist()
时,进入拦截器invoke方法,发现user.getOrderlist()是null值,执行sql查询order列表 - 把order查询出来,然后调用
user.setOrderList(List<Order> orderlist)
,接着完成user.getOrderlist()方法的调用
Powered by Waline v3.1.3