延迟加载有效,但不应该

Posted

技术标签:

【中文标题】延迟加载有效,但不应该【英文标题】:Lazy loading works, but shouldn't 【发布时间】:2017-08-14 00:27:44 【问题描述】:

这个问题的上下文在spring-boot中,使用spring-data-jpa和hibernate。

同事写了一个@Service,并将服务方法注解为@Transactional。服务方法加载一个实体,然后点击一个一对多的延迟加载集合 (fetch = FetchType.LAZY)。服务方法由一些自定义委托人调用,我将在后面讨论。当从 @RestController 端点调用时,这可以正常工作。

当我从骆驼路由(再次通过自定义委托)调用服务时,它遇到了延迟初始化异常。

在挖掘中,发现服务实现了一个接口,自定义委托人查找服务(它被注入所以有适当的代理)并调用一个方法 在实际上是 java-8 默认方法的接口上。然后这个默认方法在本地调用@Transactional 方法。

所以有问题:- 这是一个本地方法调用,所以 @Transactional 注释的方面/代理没有完成(我们使用 aspectJAutoProxy)所以该方法不在事务中调用,所以惰性 -加载应该失败。并仔细检查,还通过@Scheduled 注释进行了尝试:相同的行为。像它应该的那样呕吐。

我的问题:那么为什么从@RestController 调用它时会起作用?这让我发疯了!

rest 控制器端点上没有事务注释。

我使用TransactionSynchronizationManager.isActualTransactionActive() 向服务添加了一些调试代码,它表明在任何情况下都不存在事务,即使通过控制器端点调用也是如此。

那么为什么延迟加载在从控制器调用时会起作用? 我转储了所有 SQL,并且在任何时候都没有加载惰性集合,因此它们不在任何休眠缓存中。

我记得有一次读到延迟加载是一个提示,而不是一个命令,但仍然......为什么它在那种情况下有效?

【问题讨论】:

您是否使用 Open session in View (OSIV)? 感谢 Ali,不使用 OSIV。有一个过滤器可以从数据库加载标头中指定的经过身份验证的用户,但该事务在休息控制器代码执行之前关闭。为了验证,服务中的 TransactionSynchronisationManager.isActualTransactionActive() 说是假的。 你找到答案了吗?它也困扰着我。我有一个 dao 层,我在其中加载东西,当我在控制器中迭代它时,它会延迟加载而不是失败(我想要) 抱歉,马里奥,一直没有时间解决这个问题。而关于 java8 默认方法的原始问题中的所有废话都与此无关。我随后在最简单的情况下遇到了同样的问题:延迟加载在不应该的情况下通过休息控制器工作;它不能通过其他代码路径工作(这很好)。 @MarioB 找到答案! 【参考方案1】:

在多次被这个困惑之后,偶然发现了答案:

sprint-boot 正在通过 OpenEntityManagerInView 拦截器在我们背后执行 open-entity-manager-in-view。不知道这是怎么回事。

请参阅 Vlad Mihalcea https://***.com/a/48222934/208687 的出色回答

【讨论】:

【参考方案2】:

当您的方法在返回方法后注释到事务性休眠会话关闭时,如果从方法返回的对象具有惰性,则惰性属性不会加载,并且您会收到会话关闭的异常。您可以在查询中使用 fetch 或使用 OSIV

【讨论】:

谢谢阿里,我明白了。我不是在问为什么延迟加载失败。我在问为什么它从一个特定的代码路径(通过 spring-rest-controller)工作,据我所知,它不应该工作。

以上是关于延迟加载有效,但不应该的主要内容,如果未能解决你的问题,请参考以下文章

延迟加载

11.延迟加载

MyBatis_延迟加载

延迟作业:如何在开发模式下的每次调用期间重新加载有效负载类

JPA 选择,啥应该延迟加载,当不需要时

Mybatis-延迟加载