LazyInitializationException 有哪几种修法?各自的 SQL 条数、连接占用和代价是多少?open-in-view 和 enable_lazy_load_no_trans 能不能当解药?这篇在 Spring Boot 4.0.8 上用同一份数据逐个测。
Controller 里直接 findAll() 再访问 children(没有事务):成功,统计到子对象 20 个 | 执行 SQL 6 条,加载实体 25 个,打开 Session 0 个,返回前连接池 active=1 Service 方法加 @Transactional(readOnly = true),里面访问 children:成功,统计到子对象 20 个 | 执行 SQL 6 条,加载实体 25 个,打开 Session 0 个,返回前连接池 active=1 join fetch 查询:成功,统计到子对象 20 个 | 执行 SQL 1 条,加载实体 25 个,打开 Session 0 个,返回前连接池 active=1 @EntityGraph(attributePaths = "children"):成功,统计到子对象 20 个 | 执行 SQL 1 条,加载实体 25 个,打开 Session 0 个,返回前连接池 active=1 DTO 投影(new ParentDto(id, name, count(c))):成功,统计到子对象 20 个 | 执行 SQL 1 条,加载实体 0 个,打开 Session 0 个,返回前连接池 active=1 启动日志里有没有 open-in-view 的警告:True
open-in-view=true:不报错,但连接被占着,N+1 是 6 条 SQL。
Controller 里直接 findAll() 再访问 children(没有事务):异常 LazyInitializationException:Cannot lazily initialize collection of role 'demo.Parent.children' with key '1' (no session) | 执行 SQL 1 条,加载实体 5 个,打开 Session 1 个,返回前连接池 active=0 Service 方法加 @Transactional(readOnly = true),里面访问 children:成功,统计到子对象 20 个 | 执行 SQL 6 条,加载实体 25 个,打开 Session 1 个,返回前连接池 active=0 join fetch 查询:成功,统计到子对象 20 个 | 执行 SQL 1 条,加载实体 25 个,打开 Session 1 个,返回前连接池 active=0 @EntityGraph(attributePaths = "children"):成功,统计到子对象 20 个 | 执行 SQL 1 条,加载实体 25 个,打开 Session 3 个,返回前连接池 active=0 DTO 投影(new ParentDto(id, name, count(c))):成功,统计到子对象 20 个 | 执行 SQL 1 条,加载实体 0 个,打开 Session 1 个,返回前连接池 active=0
直接访问抛 no session;事务方法 6 条,join fetch、EntityGraph、DTO 1 条。
Controller 里直接 findAll() 再访问 children(没有事务):成功,统计到子对象 20 个 | 执行 SQL 11 条,加载实体 30 个,打开 Session 6 个,返回前连接池 active=0 Service 方法加 @Transactional(readOnly = true),里面访问 children:成功,统计到子对象 20 个 | 执行 SQL 6 条,加载实体 25 个,打开 Session 1 个,返回前连接池 active=0 join fetch 查询:成功,统计到子对象 20 个 | 执行 SQL 1 条,加载实体 25 个,打开 Session 1 个,返回前连接池 active=0 @EntityGraph(attributePaths = "children"):成功,统计到子对象 20 个 | 执行 SQL 1 条,加载实体 25 个,打开 Session 3 个,返回前连接池 active=0 DTO 投影(new ParentDto(id, name, count(c))):成功,统计到子对象 20 个 | 执行 SQL 1 条,加载实体 0 个,打开 Session 1 个,返回前连接池 active=0
压住异常,但 11 条 SQL、6 个 Session。