【问题标题】:How to get around LazyInitializationException in scheduled jobs?如何在计划的作业中绕过 LazyInitializationException?
【发布时间】:2011-02-28 21:05:37
【问题描述】:

我正在开发一个部署在 Tomcat 上的 J2EE 服务器应用程序。我使用 Spring 源代码作为 MVC 框架,使用 Hibernate 作为 ORM 提供者。我的对象模型有很多惰性关系(根据请求获取依赖对象)。高级设计就像服务级方法调用一些DAO方法来执行数据库操作。从 Flex UI 或作为计划作业调用服务方法。当从 Flex UI 调用它时,服务方法工作正常,即它使用 DAO 方法获取一些对象,甚至延迟加载也能工作。这可以通过使用配置了 UI servlet 的 OpenSessionInViewFilter 来实现。

但是当调用同一个服务方法作为调度的 Job 时,它会给出 LazyInitializationException。我无法配置 OpenSessionInViewFilter,因为没有与之关联的 servlet 或 UI 请求。

我尝试围绕计划的作业方法配置事务,以便服务方法启动事务并且所有 DAO 方法都参与同一事务,希望事务将保持活动状态并且休眠会话将可用。但它不起作用。

请建议是否有人能够使这样的配置正常工作。如果需要,我可以发布 Hibernate 配置和日志消息。

非常感谢您的帮助!

史瑞郎

【问题讨论】:

  • 您能发布一下您是如何围绕计划的作业包装事务的吗?哪个部分不工作 - 它没有打开交易还是交易过早关闭?
  • 为了回答您的评论,我试图使用切入点表达式将计划的作业方法包装在它自己的新事务中。但是,切入点在开始执行时并没有真正选择我的方法,因此 Transaction 根本没有启动。

标签: hibernate spring lazy-loading


【解决方案1】:

在阅读了一些 Spring Transaction 参考资料后,我终于能够解决我的问题。 早些时候,我试图编写一个切入点表达式来在事务中运行我计划的 jon。但是切入点是使用 OR (||) 并且不知何故它在开始执行时没有选择我的方法。因此,交易从未开放。 (我的 DAO 方法已经有了一个工作切入点表达式,我正试图在该切入点中添加我计划的方法。)

然后我找到了一种以编程方式打开事务的方法,它解决了我的问题。为了他人的利益,这里是代码sn-p来做到这一点:

       transactionTemplate.execute(new TransactionCallbackWithoutResult() {
          protected void doInTransactionWithoutResult(TransactionStatus status) {
                // Invoke the real method inside transaction context
                doWork();
          }
        });

有关TransactionTemplate 的详细信息,请参阅此处的 Spring Source 文档: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html#transaction-programmatic

【讨论】:

  • 我现在收到cannot commit, transaction is rollbackOnly 异常。知道如何解决这个问题吗?
【解决方案2】:

OpenSessionInViewFilter 里面的代码并不复杂,我已经成功修改了源代码以满足自己的需要,它真正需要的只是发生延迟加载的线程。

我也将 Quartz 用于许多预定的作业,它与容器管理的事务配合得非常好。为了确保在代码退出事务边界之前加载惰性集合,我使用了几次技巧,那就是创建一个专用方法,在其中调用必要的方法:

@Transactional
private SomeOobj getSomeObj(Long id) {
  SomeObj o = someObjDao.find(id);
  o.getLazyCollection().size(); // <- load the collection
  return o;
}

在这些情况下,最好在第一次获取对象时立即加载所需的数据,但我知道这并不总是一种选择。祝你好运。

【讨论】:

    【解决方案3】:

    OpenSessionInViewFilter反正是有点坑爹了,太依赖也不好。

    一个更好的通用解决方案是编写您的休眠查询,以便急切地获取数据,而不是懒惰地获取数据。这意味着使用fetch joins

    如果这不可行或不可行,那么您的计划任务可以在 transaction 内执行整个工作。这应该让会话保持打开状态,直到它完成。

    【讨论】:

    • 是的,我试图在事务中执行该作业,但无法通过切入点表达式使其工作。交易没有开始。因此我发布了这个问题。但感谢您确认我正朝着正确的方向前进。
    猜你喜欢
    • 1970-01-01
    • 2015-04-08
    • 1970-01-01
    • 2022-12-06
    • 2022-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多