MyBatis延迟加载需精准配置:全局启用lazyLoadingEnabled=true且aggressiveLazyLoading=false,关联映射中显式设fetchType="lazy",实体类不可为final、getter不可重写、SqlSession须保持开启,并避免N+1查询。

MyBatis 延迟加载不是“一开就快”,而是要配得准、用得稳、避得开陷阱。它本质是让关联数据“按需加载”——查用户时只查用户,调用 user.getOrders() 时才去查订单,避免一次拉几十条用不上的数据。
全局开关必须配对开启
延迟加载默认完全关闭,仅设 lazyLoadingEnabled=true 不够,必须同时关闭激进模式:
- lazyLoadingEnabled = true:打开延迟加载总闸门,没它所有局部配置都失效
-
aggressiveLazyLoading = false:防止调用
toString()、equals()或任意 getter 就把全部关联(订单、地址、头像)一股脑查出来
这两项必须写在 mybatis-config.xml 的 <settings> 中,缺一不可。
关联映射里显式声明 lazy
全局开了只是铺路,每个具体关系还得“点名申请”延迟加载:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
立即学习“Java免费学习笔记(深入)”;
- 一对多用
<collection>:<collection property="orders" ofType="Order" select="selectOrdersByUserId" column="id" fetchType="lazy"/> - 一对一用
<association>:<association property="profile" javaType="Profile" select="selectProfileByUserId" column="id" fetchType="lazy"/>
fetchType="lazy" 是局部开关,优先级高于全局配置;不写则按全局设置走。
代理能工作的三个硬前提
MyBatis 靠动态代理实现延迟,以下任一不满足都会报错或失效:
- 被关联的实体类不能是
final(否则无法生成子类代理) - 关联属性的 getter 方法不能被重写、不能加逻辑、不能屏蔽(代理靠拦截它触发查询)
- 调用 getter 时,
SqlSession必须仍处于开启状态(事务未提交、未关闭;否则抛 “No session” 异常)
避开 N+1 查询这个坑
延迟加载容易误用成 N+1:循环中反复调用 user.getOrders(),导致查 1 次用户 + N 次订单 SQL。
- 批量场景下,改用
JOIN+resultMap手动映射,一次 SQL 拉全量 - 或先查出所有用户 ID 列表,再用
IN一次性查订单,手动组装关联关系 - 看日志:如果出现大量重复的
select * from order where user_id = ?,就是典型 N+1 信号


















