面试题: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 执行时机,避免不必要的大查询,提升系统性能。

相关推荐
momo14 小时前
Java+WebAI黑马知识点
java·spring boot·mybatis
snow@li19 小时前
MyBatis:动态 SQL 全景梳理
数据库·sql·mybatis
就改了21 小时前
Mybatis快速入门大全(详细版)
java·spring boot·mybatis
qq_171538851 天前
深入浅出 MyBatis XML:从入门到精通
xml·java·mybatis
万亿少女的梦1681 天前
基于SSM、JSP与MySQL的秦皇岛旅游服务管理系统设计与实现
mysql·mybatis·ssm·jsp·spring mvc
jaysee-sjc1 天前
【JavaWeb】JDBC与Mybatis零基础入门详解
java·开发语言·前端·mybatis
dear_bi_MyOnly2 天前
【MyBatis 操作数据库】
java·数据库·学习·mybatis·学习方法
用户3126874877202 天前
分页插件到底怎么拦截 SQL?MyBatis-Plus 插件机制原理一次讲透
mybatis
马优晨2 天前
pring Boot + MyBatis + Redis 项目分层架构详解 —— 各层职责、关系与工作流程
架构·mybatis·spring项目分层架构详解·spring项目架构详解·spring各层职责·spring关系与工作流程
Listen·Rain2 天前
用AI开发出一个AI
java·人工智能·spring boot·tomcat·intellij-idea·mybatis·visual studio