fetchtype=lazy是mybatis配合lazyloadingenabled=true和resultmap中association/collection节点启用的触发式延迟加载策略,仅在首次调用代理对象getter时执行预定义sql。

MyBatis 的 fetchType=lazy 是什么,它真能“按需查”?
它不是自动开启的魔法开关,而是配合 lazyLoadingEnabled=true 和关联映射(<association></association> / <collection></collection>)才生效的触发式加载策略。没配对全局开关,写再多 fetchType=lazy 也没用。
常见错误现象:fetchType=lazy 写了,但关联对象一查主表就立刻被查出来;或者干脆报 org.apache.ibatis.executor.ExecutorException: Cannot load lazy result... —— 基本是 SqlSession 已关闭或代理对象被序列化导致。
- 必须在
mybatis-config.xml或 Configuration 中显式开启:lazyLoadingEnabled=true -
aggressiveLazyLoading=false(MyBatis 3.4.1+ 默认值)才允许按需触发;设为true会“激进懒加载”,一访问任意属性就加载全部关联,失去按需意义 - 只对
resultMap中定义的<association></association>和<collection></collection>生效,且这些节点必须声明fetchType="lazy"
为什么加了 fetchType=lazy 还是立即查询了?
最常见原因是:你用了 select * from user 这类直接返回 POJO 的 select,而不是通过 resultMap 映射,并在其中配置关联节点。MyBatis 只有在 resultMap 级别才能注入代理逻辑。
另一个隐蔽坑:你在 Service 层 return 之前,不小心调用了 lazy 字段的 getter(比如 user.getOrders().size()),而此时 SqlSession 还开着——它就会当场触发 N+1 查询。
- 确认 SQL 语句绑定的是
resultMap,不是resultType - 检查是否在 mapper 接口方法上误用了
@Select注解直写 SQL,绕过了 resultMap 解析 - 避免在 SqlSession 关闭前主动访问 lazy 字段;更别在 controller 返回 JSON 时让 Jackson 触发 getter(会强制加载)
fetchType=lazy 触发时到底发生了什么?
MyBatis 不是靠字节码增强或运行时织入,而是用 JDK 动态代理生成目标对象的子类(如 User$$EnhancerByCGLIB$$...),把关联字段设为 null,并在首次调用其 getter 时,用原 SqlSession(必须还活着)去执行预定义的 select 语句(即 association 的 select 属性指向的 statement)。
所以它本质是“延迟执行一条新 SQL”,不是缓存或 join 优化。性能影响取决于那条懒加载 SQL 本身是否高效。
- 懒加载 SQL 的参数来自主对象的字段(如
userId),由column属性指定,务必确保该字段在主查询结果中存在 - 如果懒加载 SQL 涉及多表 join 或未命中索引,反而比一次性 join 更慢
- 事务范围必须覆盖整个懒加载过程,否则 SqlSession 关闭后代理无法执行查询
Spring + MyBatis 下容易被忽略的致命点
Spring 默认的 @Transactional 方法执行完就 commit 并 close SqlSession,但如果你在 controller 层 return 一个含 lazy 字段的 VO,Jackson 序列化时才触发 getter——此时 Session 早没了,直接抛异常。
这不是 MyBatis 的 bug,是使用边界没对齐。
- 要么在 service 层主动调用一次 lazy 字段(如
user.getOrders().size()),确保加载完成再返回 - 要么用
@Transactional(propagation = Propagation.REQUIRED)把 controller 方法也包进去(不推荐,破坏分层) - 更稳妥的是:禁用懒加载,改用
fetchType=eager+JOIN查询,或手写select显式控制关联数据获取时机
懒加载真正适合的场景其实很窄:大对象树中只有极少数分支会被实际访问,且你能完全掌控生命周期。多数业务里,它带来的不确定性远大于节省的几毫秒查询时间。










