【问题标题】:Lazy load handling (Hibernate + Spring MVC) [closed]延迟负载处理(Hibernate + Spring MVC)[关闭]
【发布时间】:2014-01-24 15:56:06
【问题描述】:

在 Spring MVC 应用程序中处理延迟加载对象的最佳解决方案是什么?我已经对该主题进行了一些搜索,并找到了以下解决方案:

在视图中打开会话:为每个请求打开一个会话并在视图呈现后关闭它。这个解决方案的问题是我需要在 Spring MVC 模型之外延迟加载对象(例如 Junit 测试用例)。关于此解决方案的另一个讨论问题是异常处理。如果事务在视图渲染过程中抛出异常怎么办?

显式打开会话:每当我需要延迟加载对象时显式打开会话。实际上这个解决方案应该可行,但我认为这不是正确的方法。

使用 AOP: 创建一个在会话中包装延迟加载方法的切面。这可能是一个解决方案,但我不知道我应该在应用程序的哪个级别定义 poitcuts

创建自定义查询:为延迟加载创建查询和为急切加载创建查询。这个解决方案确实有效,但在我看来,延迟加载模式的应用是错误的

【问题讨论】:

  • 查看这篇关于 OSIV 优点/缺点的帖子 blog.jhades.org/open-session-in-view-pattern-pros-and-cons
  • 使用自定义查询的问题是它们强制连接到相关对象上,这并不总是最佳解决方案。有时,对主要对象进行 1 次查询,对相关对象进行 N 次单独查询会更有效。
  • 此外,将查询与延迟加载分开很好,因为这意味着您可以使用单一方法进行延迟加载(例如: loadRelationsRequiredByDefault() ),可以被许多不同的查询重用返回同一个对象(或对象列表)。

标签: java spring hibernate spring-mvc


【解决方案1】:

在所有情况下都没有更好的解决方案,问题是服务层上的@Transactional 在渲染阶段开始时不会保持会话打开。

在渲染开始之前,会话被刷新,事务被提交并且会话被关闭。

解决此问题的一种方法是使用自定义查询来加载每个时刻所需的数据,具体取决于正在构建的视图。

另一种方法是在视图中使用open session,它在渲染时保持会话打开,但由于无意使用延迟加载,可能会导致应用程序中出现N+1问题。

同样在视图中打开会话可能会导致不可重复读取的问题,其中一些数据被服务层读取并用于提交事务,但是当视图呈现开始时,数据不再可用或被修改,并且它对构建视图很重要。

请参阅 JBoss Seam 团队的 post,了解如何使用 OSIV 解决这些问题(Seam 是由许多与 Hibernate 相同的开发人员开发的)。

不同的方法有不同的优缺点,具体取决于项目的优先级。如果不必编写自定义查询的便利性很重要,因为有很多查询要编写,那么 OSIV 是一个不错的选择。偶尔的 N+1 问题可以逐案解决并忍受。

如果重点是控制查询,因为应用程序对性能至关重要,那么自定义查询是一种选择。

确实没有明确的最佳解决方案。如果您使用在客户端(例如 angular.js)而不是服务器上运行的视图技术,那么您就不会遇到这类问题,因为不涉及服务器端渲染。

【讨论】:

    【解决方案2】:

    我不确定它是否符合您的要求,而不是它是否是“正确的”处理方法,但我使用了@Transactional 注释。所以我会:

    @Autowired
    AccountDAO accountDAO; 
    
    @Transactional
    public List<String> getNamesOfAccount(String accountName){
        Account account = accountDAO.get(accountName);
        return account.getNames();
    }
    

    Account 中的 names 属性是惰性的,并且在返回之前加载。 如果有更好的方法让我们听到!

    【讨论】:

    • 这可能是一个解决方案。我不知道这是否是最好的方法。我会等待其他人的建议。
    • 该解决方案的优点在于,您的 DAO 中只有 EntityManager / SessionFactory。如果您不调用 getNames() ,则不会加载任何内容。如果你这样做了,那么 Spring 会为你做所有事情,而你不必打开 Session 或其他东西。
    • 不能说服我的是,我无法通过域对象接口访问“惰性字段”,而只能通过 DAO。
    • 为什么?在我的情况下,帐户是一个域对象。并且有一次需要加载。惰性字段“names”的访问是没有dao的
    • 如果您加载一个 Account 实例,您将无法访问事务边界之外的字段“names”,除非在“getNamesOfAccount”之前调用,否则您会遇到 LazyInitializationException
    猜你喜欢
    • 1970-01-01
    • 2011-05-11
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 2012-01-24
    • 1970-01-01
    • 1970-01-01
    • 2011-02-27
    相关资源
    最近更新 更多