关于MyBatis 的懒加载(Lazy Loading)机制

发布于:2025-07-31 ⋅ 阅读:(18) ⋅ 点赞:(0)

目录

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 {
    @Autowired
    private 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 ... 的SQL
        System.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


网站公告

今日签到

点亮在社区的每一天
去签到