如何使用Spring和Hibernate为Web应用程序和批处理作业设置事务

时间:2010-10-07 15:00:00

标签: hibernate spring web-applications transactions jobs

我有一个使用Spring 2.5和Hibernate 3的应用程序。

有一个带有表示层,服务层和DAO层的Web应用程序,以及一些共享相同服务和DAO层的Quartz作业。

使用@Transactional注释在不同的层中初始化事务,如下所示:

alt text

这让我想到了我在这里描述的一个问题:Controlling inner transaction settings from outer transaction with Spring 2.5

我读了一些关于如何设置事务以将Spring和Hibernate连接在一起的内容。看起来推荐的方法是初始化服务层中的事务。

我不喜欢的是大多数交易只是因为hibernate正常工作所必需的。

当我真的需要一个调用多种服务方法的作业的事务时,似乎我没有选择继续从作业初始化事务。因此,将@Transactional注释从DAO移动到服务似乎没有任何区别。

您如何建议为此类应用设置交易?

1 个答案:

答案 0 :(得分:5)

  

我读了一些关于如何设置事务以将Spring和Hibernate连接在一起的内容。看起来推荐的方法是初始化服务层中的事务。

当然。事务划分应在服务层级别完成,而不是在DAO层级别完成:

  • 工作单位是服务,而不是DAO
  • 如果需要,您希望事务跨越多个DAO。
  

我不喜欢的是大多数交易只是因为hibernate正常工作所必需的。

您应该详细说明这一部分,因为事务不是特定于Hibernate的。

  

当我真的需要一个调用多种服务方法的作业的事务时,似乎我没有选择继续从作业初始化事务。

如果要在从Job层启动的事务中调用多个服务,请将您的服务声明为具有REQUIRED语义的事务(默认)并依赖Spring事务传播(除非您需要远程调用,否则这适用) ;在这种情况下,使用EJB)。

  

所以将@Transactional注释从DAO移动到服务似乎没有任何区别。

确实有所作为,而且您需要在运行批处理时从作业层启动事务这一事实不会产生任何不同。

我热烈建议您阅读Chapter 9. Transaction management


  

(...)我的主要问题来自Hibernate。对不起,如果我不清楚。

没问题。只是当问题含糊不清时,你经常得到一个模糊的答案:)

  

来自hibernate文档:“数据库事务永远不是可选的。与数据库的所有通信都必须在事务中进行。”这就是为什么开发人员将DAO方法交易放在我的项目上的原因。

很抱歉,但上述声明只表示“与数据库的通信必须在内部事务”,仅此而已,并且决定在哪里开始交易还剩下由您自行决定(通常是服务层)。如果您在DAO级别执行此操作,如果MySuperService调用DaoFooDaoBarDaoBar失败,该怎么办?在这种情况下,您可能希望回滚所有更改,而不仅仅是在DaoBar中执行的更改。因此需要控制工作单元开始的交易。

恕我直言,开发人员需要一些指导。

  

这是否意味着我的所有服务都应该是交易的?即使我只是阅读数据?

首先,我建议您阅读Non-transactional data access and the auto-commit modeSessions and transactions的小弟弟)来澄清有关“只读交易”的内容。阅读整个页面是值得的,但让我引用这个特定的部分:

  许多应用程序开发人员都认为他们   可以和a之外的数据库交谈   交易。这显然不是   可能; 无法发送任何SQL语句   到数据库之外的数据库   交易即可。术语非交易   数据访问意味着没有   显式交易边界,没有   系统事务,那个   数据访问的行为是   自动提交模式。这并不意味着没有   物理数据库事务是   参与。

完成上述链接后,下一个建议的阅读将为@Transactional read-only flag pitfalls。以下是相关部分:

  

(...)最重要的是当你   使用基于ORM的框架,   只读标志是非常无用的   大多数情况被忽略。但如果你   仍然坚持使用它,总是设置   传播方式为SUPPORTS,如   如清单9所示,所以没有事务   开始了:

     

清单9.使用只读和SUPPORTS传播模式进行选择   操作

@Transactional(readOnly = true, propagation=Propagation.SUPPORTS)
public TradeData getTrade(long tradeId) throws Exception {
   return em.find(TradeData.class, tradeId);
}
     

更好的是,只是避免使用   完全@Transactional注释   在进行读操作时,如图所示   在清单10中:

     

清单10.删除select的@Transactional注释   操作

public TradeData getTrade(long tradeId) throws Exception {
   return em.find(TradeData.class, tradeId);
}