关于MyBatis 的懒加载(Lazy Loading)机制
目录
1、定义
1.1、什么是懒加载
1.2、懒加载实现原理
1.3、懒加载的配置
1.4、常见应用场景
2、加载时机分类
2.1、直接加载
2.2、侵入式延迟
2.3、深度延迟
3、实现
4、注意事项
前言
懒加载可以提高数据库性能,MyBatis延迟加载的策略是先从单表查询然后再从关联表查询,这样可以大大提高数据库性能,单表查询要比关联查询多张表速度要快。
resultMap可以实现高级映射,即使用association和collection实现一对一、一对多的映射,association和collection具备懒加载的功能。
关于mybatis的更多介绍,可参考:
关于Mybatis和JDBC的联系_mybatis-plus 执行jdbc 方法-CSDN博客文章浏览阅读1k次,点赞26次,收藏28次。前言基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。_mybatis-plus 执行jdbc 方法https://dyclt.blog.csdn.net/article/details/147553601?spm=1011.2415.3001.5331关于mybatis的执行流程,如下图所示:
1、定义
1.1、什么是懒加载
懒加载(Lazy Loading),又称延迟加载,是指在需要用到某个数据时才去真正查询,而不是在一开始就加载所有关联数据。
这样可减少不必要的数据库访问,提升系统性能,特别是在有一对多、多对一等关联关系时很有用。
1.2、懒加载实现原理
1、MyBatis 支持对关联对象(一对一、一对多映射)进行懒加载。
2、只有当程序第一次访问被懒加载的属性时,才会发送 SQL 查询数据库,获取该属性的数据。
3、通过动态代理的方式实现:代理对象会拦截 getter 方法,触发 SQL 查询。
1.3、懒加载的配置
1、全局配置
需要在 mybatis 的配置文件(mybatis-config.xml 或 application.yml 等)中开启懒加载:
<settings><setting name="lazyLoadingEnabled" value="true"/><!-- 全局开关,开启后支持关联对象懒加载 --><setting name="aggressiveLazyLoading" value="false"/><!-- true表示只要访问任意属性就会全部加载;false表示只加载访问的属性(推荐) -->
</settings>
2、局部配置
如下所示:
<association property="teacher" javaType="Teacher" column="t_id" select="getTeacher" fetchType="lazy"/> <!-- 显式指定懒加载 -->
1.4、常见应用场景
1、一对一和一对多关联映射时,避免主表加载后立刻加载所有子表数据。
2、仅在确实需要用到子对象/集合时才去查库,提高性能。
2、加载时机分类
MyBatis根据对关联对象查询的select语句的执行时机,分为三种类型:直接加载、侵入式延迟加载与深度延迟加载。
2.1、直接加载
执行完对主加载对象的 select 语句,马上执行对关联对象的 select查询。
2.2、侵入式延迟
执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。
2.3、深度延迟
执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。
只有当真正访问关联对象的详情时,才会执行对关联对象的 select查询。
⚠️注意:
延迟加载的应用要求,关联对象的查询与主加载对象的查询必须是分别进行的 select 语句,不能是使用多表连接所进行的select查询。
因为多表连接查询,其实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。
如下所示:
正确方式(支持延迟加载)
- 第一次查订单:select orders.id..... from orders where id = ?
- 访问order.getUser()时查用户:select * from user where orders.id = ?
不支持延迟加载(多表连接,JOIN查询)
- 直接查订单及用户:select o.*,u.* from orders o left join user u on o.user_id = u.id where o.id = ?
在第二种写法中,订单和用户数据都在第一条SQL就查出来了,无法实现访问order.getUser()时再查用户,你的user属性根本没有“延迟加载的机会”,所以用JOIN多表查询,不支持懒加载。
MyBatis中对于延迟加载设置,只对于resultMap中的collection和association起作用,可以应用到一对一、一对多、多对一、多对多的所有关联关系查询中。
3、实现
3.1. 关于表信息
我们以如下数据库表为例:
- User 用户表
- Order 订单表,order.user_id 指向 user.id
2. Java实体类
// User.java
public class User {private Integer id;private String name;// 省略 getter、setter
}// Order.java
public class Order {private Integer id;private String product;private Integer userId;private User user; // 关联用户// 省略 getter、setter
}
3. Mapper XML(OrderMapper.xml)
<resultMap id="orderResultMap" type="Order"><id property="id" column="id"/><result property="product" column="product"/><result property="userId" column="user_id"/><!-- 懒加载user属性 --><association property="user"javaType="User"column="user_id"select="com.example.mapper.UserMapper.selectById"fetchType="lazy"/>
</resultMap><!-- 查询订单 -->
<select id="selectOrderById" resultMap="orderResultMap">SELECT * FROM orders WHERE id = #{id}
</select>
注意:fetchType="lazy"配合全局懒加载配置,association 标签会使用懒加载。
4. UserMapper.xml
<select id="selectById" resultType="User">SELECT * FROM user WHERE id = #{id}
</select>
5. service/测试代码
public class OrderService {@Autowiredprivate OrderMapper orderMapper;public void demoLazyLoad(Integer orderId) {Order order = orderMapper.selectOrderById(orderId);System.out.println("订单商品:" + order.getProduct());// 此时 User 还没有被加载// 如果没有访问 order.getUser(),不会触发 user 查询User user = order.getUser(); // 这里才会发出 select user ... 的SQLSystem.out.println("用户名称:" + user.getName());}
}
执行流程:
1、
Order order = orderMapper.selectOrderById(orderId);
只查orders表,不会查user表。
2、
order.getProduct()依然不会查user表
3、
order.getUser()此时才会发起:Select * from user where id=xx
4、注意事项
- 懒加载会带来更多SQL查询,避免N+1问题时要注意(如循环访问每个 order 的 user)。
- 如果在事务、连接关闭以后才访问懒加载属性,会抛异常(因为数据源已经关闭)。
- 部分分页插件也会影响懒加载。
- 可以用fetchType="eager" 强制立即加载。
总结
MyBatis懒加载本质是通过动态代理,实现对指定属性的延迟查询。
- 开启懒加载:lazyLoadingEnabled=true
- 推荐:aggressiveLazyLoading=false 配合 association/many 标签
- 注重 N+1 问题和连接生命周期。
参考文章:
1、Mybatis懒加载_mybatis的懒加载-CSDN博客文章浏览阅读411次,点赞10次,收藏8次。本文介绍了MyBatis中的延迟加载机制,包括直接加载、侵入式延迟加载和深度延迟加载,以及如何在一对多查询中实现延迟加载,通过示例展示了如何在XML配置和代码中启用和调整延迟加载策略。https://blog.csdn.net/wming666/article/details/135888383?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522fd91d681fe4194d094c1c353d1b9600f%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=fd91d681fe4194d094c1c353d1b9600f&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-135888383-null-null.142^v102^control&utm_term=mybatis%E7%9A%84%E6%87%92%E5%8A%A0%E8%BD%BD&spm=1018.2226.3001.4187