跳至主要內容

MyBatis-延迟加载

holic-x...大约 4 分钟JAVA框架

MyBatis-延迟加载

学习核心

  • MyBatis的延迟加载概念

学习资料

MyBatis-延迟加载核心open in new window

延迟加载概念

MyBatis的延迟加载

​ MyBatis支持延迟加载,但是默认没有开启。

​ 延迟加载:需要用到数据的时候才进行加载,如果不需要用到数据则不加载

  • MyBatis支持一对一、一对多关联的延迟加载
  • 在MyBatis核心配置文件中可设定延迟加载开关(lazyLoadingEnabled)

场景案例

​ 例如一个用户对用多个订单,此时可以构建用户-订单(1:n)的关系,在查询的时候有如下情况

  • 立即加载:查询用户的时候,将用户所属的订单数据页查询出来

  • 延迟加载:查询用户的时候,暂时不查询订单数据,但需要订单的时候再去查询订单(按需加载)

image-20240614111647087

​ 配置懒加载的方式有两种,一种是全局配置(在核心配置文件中开启懒加载),一种是局部配置(在对应的映射配置中开启懒加载)

  • 全局配置(在核心配置文件中开启延迟加载开关)
	<!-- 配置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方法进行封装)

image-20240614131515659

​ 参考演示效果如上所示,以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()方法的调用

image-20240614114303376

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3