一、延迟加载是什么?
延迟加载(Lazy Loading)又称“惰性加载”,指的是:
当查询一个对象时,不立即加载它的关联对象(如一对多、多对一关系),而是在第一次真正使用该关联对象时才去执行 SQL 查询加载它。
举个例子:
User user = userMapper.selectById(1);
// 此时只查了 user 表,不查 order 表
user.getOrders(); // 这一步才去执行查询 orders 的 SQL
这样可以避免一次性加载大量无关数据,提高查询性能。
二、如何开启延迟加载?
在 MyBatis 的mybatis-config.xml中配置:
<settings><!-- 全局启用延迟加载 --><setting name="lazyLoadingEnabled" value="true"/><!-- 设置为 true 时,所有关联对象都会延迟加载 --><setting name="aggressiveLazyLoading" value="false"/>
</settings>
aggressiveLazyLoading:
true(旧版本默认):访问任意属性时会触发所有懒加载属性;false:只在访问对应属性时才加载(推荐,性能更好)。
然后在映射文件中配置关联关系:
<resultMap id="userMap" type="User"><id property="id" column="id"/><result property="name" column="name"/><collection property="orders"select="selectOrdersByUserId"column="id"/>
</resultMap>
此时,当访问user.getOrders()时,才会触发对Order的查询。
lazyLoadingEnabled配置是全局开关,也可以在单个映射关系上通过属性fetchType="lazy"来开启懒加载:
<resultMap id="userMap" type="User"><id property="id" column="id"/><result property="name" column="name"/><collection property="orders"fetchType="lazy"select="selectOrdersByUserId"column="id"/>
</resultMap>
三、实现原理(源码层面)
延迟加载的核心为动态代理机制。
查询阶段:创建代理对象
MyBatis 在查询阶段会扫描每个ResultMap的映射字段,判断哪些字段需要懒加载,然后为结果对象创建代理。
在DefaultResultSetHandler.handleResultSets()里,MyBatis 读取 ResultSet 时会调用getRowValue():
![]() |
这个方法会为每一行创建 Java 对象(例如User),并填充属性。注意其中的createResultObject方法和applyPropertyMappings方法,createResultObject创建了结果对象的动态代理对象:
![]() |
![]() |
![]() |
注意createResultObject方法接收lazyLoader作为参数,此时lazyLoader内部是个空集合。
下一步applyPropertyMappings方法才在lazyLoader中添加用于加载不同属性(可能有多个关联属性)的ResultLoader:
![]() |
![]() |
![]() |
这样返回的user实际上是一个代理对象,它的某些属性还没被真正赋值。
访问阶段:触发加载
当我们在 Java 代码中第一次调用:
user.getOrders();
其方法被代理,代理逻辑封装在EnhancedResultObjectProxyImpl,其intercept方法中,使用ResultLoaderMap lazyLoader加载了 Getter 方法所获取的属性:
![]() |
ResultLoaderMap#load最终通过ResultLoader#loadResult加载属性值:
![]() |
![]() |
![]() |
ResultLoader#loadResult通过Executor查询到结果:
![]() |
这样,数据就在真正访问时被加载。












