面试题:MyBatis延迟加载的底层原理

在学习 MyBatis 的过程中,延迟加载是一个高频考点,也是优化数据库查询性能的重要技巧。今天就通过两个经典面试题,把延迟加载的概念、使用方式和底层原理一次讲透。


一、MyBatis 是否支持延迟加载?

1. 什么是延迟加载?

延迟加载 (Lazy Loading)指的是:在需要用到数据时才进行加载,不需要用到数据时就不加载数据

举个例子:查询用户信息时,用户和订单往往是一对多的关系。如果我们只想查看用户的基本资料,就不必立刻把该用户的所有订单查出来,等到真正点开"我的订单"时再去查询。这种按需加载的策略,就是延迟加载。

2. MyBatis 的支持情况

MyBatis 对延迟加载提供了良好的支持,具体体现在:

  • 支持 一对一关联对象 的延迟加载(association
  • 支持 一对多关联集合对象 的延迟加载(collection

3. 如何开启延迟加载?

在 MyBatis 的核心配置文件中,可以通过 lazyLoadingEnabled 参数来控制全局的延迟加载行为:

复制代码
<settings>
    <!-- 开启延迟加载,默认值为 false -->
    <setting name="lazyLoadingEnabled" value="true"/>
</settings>

当该配置设为 true 后,所有满足条件的关联查询都会启用延迟加载。如果想对某个特定的关联查询做细粒度控制,还可以使用 fetchType 属性,它可以覆盖全局配置

一对一(association)的局部控制示例:

复制代码
<resultMap id="orderResultMap" type="Order">
    <!-- ...其他映射... -->
    <association property="user" column="user_id" 
                 javaType="User" select="findUserById" 
                 fetchType="lazy"/>
</resultMap>

一对多(collection)的局部控制示例:

复制代码
<resultMap id="userResultMap" type="User">
    <!-- ...其他映射... -->
    <collection property="orders" column="user_id" 
                ofType="Order" select="findOrdersByUserId" 
                fetchType="lazy"/>
</resultMap>

fetchType 可选值:

  • lazy:延迟加载
  • eager:立即加载

无论全局 lazyLoadingEnabled 是否开启,fetchType 都会优先起作用。比如全局关闭延迟加载,但某个关联属性用 fetchType="lazy" 修饰,那它依然是延迟加载;反之亦然。

二、延迟加载的底层原理

这个问题稍微深入一些,但理解之后能帮你更好地掌握 MyBatis 的工作机制。

MyBatis 的延迟加载,本质上是通过 动态代理 来实现的,默认使用 CGLIB 库。整个过程可以分为三步:

1. 使用 CGLIB 创建主对象的代理对象

当执行完主查询(比如根据 ID 查 User)后,MyBatis 并不会直接返回那个原始的 User 实例,而是为它创建一个 CGLIB 代理对象 。这个代理对象是 主对象(User)本身的代理,而非关联对象(如订单列表)的代理。

代理对象会拦截所有方法,尤其是那些针对延迟加载属性的 getter 方法。

2. 调用目标方法时进入拦截器

当你真正调用代理对象的某个方法(例如 user.getOrders())时,会进入 CGLIB 的 invoke 拦截器。在拦截器内部,MyBatis 会检查关联属性当前的值:

  • 如果发现该属性还是 null(或者是一个未加载的占位对象),就说明数据尚未加载。
  • 这时,拦截器会触发真实的 SQL 查询,把关联数据从数据库中查出来。

3. 获取数据后通过 setter 方法赋值

SQL 查询执行完毕后,拿到了关联数据(比如订单列表),拦截器会调用目标对象对应的 setter 方法 ,将查到的数据注入到主对象的属性中。

设置完成后,再接着执行最初被拦截的方法,此时关联属性已经有值了,程序就能正常获取到完整的数据。

整个流程用一句话概括:先把空壳对象(代理)给你,等你真正要用关联属性时,再去数据库取回数据并"填"进去。

总结

  • 什么是延迟加载:按需加载,不是立即查询所有关联数据。
  • MyBatis 支持情况 :支持一对多、一对一的延迟加载,通过全局 lazyLoadingEnabled 或局部 fetchType 配置。
  • 底层原理:基于 CGLIB 为主对象创建代理,在访问 getter 方法时触发二次查询,再将结果注入主对象,最终返回已有值的属性。

理解延迟加载不仅能帮你应对面试,更能在实际项目中合理控制 SQL 执行时机,避免不必要的大查询,提升系统性能。

相关推荐
JAVA面经实录91710 小时前
MyBatis面试题库
java·mybatis
杨运交11 小时前
[022][数据模块]基于雪花算法的 MyBatis-Plus 主键生成器设计与实现
mybatis
Mahir0813 小时前
MyBatis 深度解密:从执行流程到底层原理全解
java·后端·面试·mybatis
Mahir0818 小时前
MyBatis 分页与插件深度解密:从插件机制到三大分页方案原理全解
java·后端·mybatis·mybatis-plus·大厂面试题
谷哥的小弟20 小时前
图文详解Spring Boot整合MyBatisPlus(附源码)
mybatis·源码·springboot·mybatis-plus·整合
醉颜凉1 天前
Lucene底层原理:倒排索引实现原理与代码实战,彻底吃透搜索引擎核心
搜索引擎·mybatis·lucene
JAVA面经实录9171 天前
MyBatis学习体系
java·mybatis
墨_风1 天前
MyBatis时间区间查询异常排查(达梦数据库)
数据库·mybatis·达梦
霸道流氓气质2 天前
MyBatis 分页查询 + Feign 数据补充实战指南
数据库·oracle·mybatis
隐退山林2 天前
JavaEE进阶:MyBatis操作数据库(进阶)
数据库·java-ee·mybatis