【问题标题】:Whats the best practice around when to start a new session or transaction for batch jobs using spring/hibernate?何时使用 spring/hibernate 为批处理作业启动新会话或事务的最佳实践是什么?
【发布时间】:2011-07-17 00:02:44
【问题描述】:

我在 Java 中有一组批处理/cron 作业,它们调用我的服务类。我也在使用 Hibernate 和 Spring。

最初批处理层总是创建一个外部事务,然后批处理作业将调用一个服务以从具有相同会话的数据库中获取对象列表,然后调用一个服务来分别处理每个对象。为我的服务层设置了一个 tx-advice 来回滚任何 throwable。因此,如果第 5 个对象出现异常,则处理的前 4 个对象也会回滚,因为它们都是同一事务的一部分。

所以我认为在批处理层中创建的这个外部事务是不必要的。我删除了它,现在我调用服务来获取对象列表。然后调用另一个服务来分别处理每个对象,如果其中一个对象失败,其他对象仍然会持续存在,因为它是每个服务调用的新事务/会话。但是我现在遇到的问题是在获取对象列表之后,当我将每个对象传递给要处理的服务时,如果我尝试获取其中一个属性,我会收到延迟初始化错误,因为会话用于加载该对象(从列表中)已关闭。

我想到的一些选项是在批处理作业中获取一个 ID 列表并将每个 ID 传递给一个服务,该服务将在该会话中检索整个对象并处理它。另一种方法是将该对象的属性的延迟加载设置为 false,但这会每次都加载所有内容,即使有时不需要嵌套属性。

我总是可以回到最初使用每个批处理作业的外部事务的方式,然后在每次调用服务以处理每个单独的对象之前在批处理作业中创建另一个事务...

这样的最佳做法是什么?

【问题讨论】:

    标签: java hibernate spring session transactions


    【解决方案1】:

    我会说你列出了除OpenSessionInView 之外的所有可能选项。这将使您的会话在事务中保持活跃,但很难正确实施。太难了,以至于many 将其视为反模式。

    但是,由于您没有实现 Web 界面,也没有处理高度线程化的环境,我会说这是要走的路。这不像您将实体传递给视图。您最担心的是在遍历集合时对数据库进行 N+1 调用,但由于这是一项 cron 作业,与代码清洁度相比,性能可能不是主要问题。如果你真的很担心,只需确保通过调用可以执行 select * 的 DAO 来获取所有集合。

    此外,当您在同一事务中执行所有操作之前,您实际上是在执行 Open Session In View。在 Spring 中,会话是在每个事务的基础上打开的,因此长时间保持事务打开实际上与长时间保持会话打开相同。在您的情况下,唯一真正的区别是您可以定期提交,而不必担心以后会出现延迟初始化错误。

    编辑

    话虽如此,在 View 中设置 Open Session 需要一些时间,因此除非您对在同一事务中执行所有操作有任何特殊问题,否则您可能会考虑回到那个状态。

    另外,我刚刚注意到您提到在批处理层中打开交易,然后在服务层中打开“迷你交易”。这绝对不是一个好主意。 Spring 的注释驱动事务将搭载会话中任何当前打开的事务。这意味着如果当前打开的事务是读写的,则应该是只读的事务将突然变为读写。此外,在最外面的事务完成之前,不会刷新会话,因此用@Transactional 标记服务层没有意义。将@Transactional 放在多个层上只会给人一种虚假的安全感。

    其实我前段时间blogged about this issue

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-25
      • 2017-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-27
      相关资源
      最近更新 更多