1.1 什么是延迟加载及其工作原理
延迟加载在MyBatis中是个挺有意思的设计模式。简单来说,它就像网购时的"货到付款"——只有当你真正需要某个数据时,系统才会去数据库查询获取。这种按需加载的机制,能有效避免一次性加载过多不必要的数据。
我记得在去年做的一个电商项目中,商品详情页就遇到了性能问题。每个商品都关联着几十条评论数据,但用户通常只看前几条。当时我们启用了延迟加载,评论数据只在用户点击"查看更多评论"时才去查询,页面加载速度直接提升了40%左右。
MyBatis实现延迟加载的核心原理其实很巧妙。它通过动态代理技术,为需要延迟加载的属性创建代理对象。当你第一次调用这个对象的getter方法时,代理对象才会执行真正的数据库查询。这就像有个智能助手,平时不打扰你,只在需要时才帮你跑腿取东西。
1.2 延迟加载与立即加载的区别对比
这两种加载方式各有各的适用场景。立即加载就像去超市前写好的购物清单,一次性把所有东西都买齐;延迟加载则更像是缺什么买什么,更加灵活。

从执行时机来看,立即加载在查询主对象时就会把所有关联数据一并查询出来。比如查询订单时,同时把订单项、用户信息都加载完成。而延迟加载要"懒"得多,它只在代码真正访问关联对象时才执行查询。
数据完整性的角度也很有意思。立即加载能保证数据的强一致性,所有关联数据都在同一个事务中获取。延迟加载可能会遇到"懒加载异常",特别是在Session关闭后还尝试访问延迟加载的数据时。这个坑我相信不少开发者都踩过。
性能表现方面,立即加载在数据关联复杂时容易产生大量冗余查询。延迟加载虽然单次查询更快,但可能引发N+1查询问题——这个我们后面会详细讨论。
1.3 MyBatis延迟加载的应用场景
在实际开发中,延迟加载并不是万能的,但在某些场景下确实能发挥奇效。

大数据量关联是个典型场景。比如用户管理系统中的部门与员工关系,一个部门可能有成千上万的员工。在查看部门列表时,如果立即加载所有员工信息,系统性能肯定会受影响。这时候延迟加载就派上用场了,只有在查看具体部门详情时,才加载对应的员工数据。
树形结构数据也很适合使用延迟加载。像组织架构、商品分类这类具有层级关系的数据,通常只需要先加载顶层节点,等用户展开某个节点时再加载其子节点。这种渐进式加载方式对用户体验的提升非常明显。
复杂对象图的处理也是个好例子。有些业务对象的关系网特别复杂,像订单->订单项->商品->库存->供应商这样的长链条。如果一次性加载所有关联数据,不仅查询复杂,内存占用也很大。延迟加载让这种复杂关系的处理变得优雅很多。
不过要提醒的是,延迟加载在事务边界清晰的环境中效果最好。如果业务需要保证数据的强一致性,或者关联数据被频繁访问,可能立即加载反而是更好的选择。

我个人的经验是,在做技术选型时要根据具体的业务场景来权衡。没有绝对的优劣,只有适合与否。
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
@Select("SELECT * FROM users WHERE id IN (#{userIds})")
List
public class Course {
private Long id;
private String title;
private String description;
// 延迟加载的章节列表
private List<Chapter> chapters;
// getter/setter省略
}