我正在阅读Java EE 7的事务管理,我对嵌套事务的概念和EJBContext#setRollbackOnly()
的功能感到困惑。
假设我有两个会话Bean,Bean1Impl
和Bean2Impl
并且他们的签名是:
@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class Bean1Impl implements Bean1 {
@Resource
private EJBContext context;
@TransactionAttribute(REQUIRED)
public void method1() {
try {
//some operations such as persist(), merge() or remove().
}catch(Throwable th){
context.setRollbackOnly();
}
}
}
@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class Bean2Impl implements Bean2 {
@Resource
private EJBContext context;
@TransactionAttribute(REQUIRED)
public void method2() {
try {
//some operations such as persist(), merge() or remove().
//an exception has been thrown
}catch(Throwable th){
context.setRollbackOnly();
}
}
}
如Java EE 7 Tutorial中所述:
51.3.1.1必需属性
如果客户端在事务中运行并调用企业bean的方法,则该方法 在客户的交易中执行。如果客户不是 与事务相关联,容器启动新事务 在运行方法之前。
Required属性是所有人的隐式事务属性 使用容器管理的事务运行的企业bean方法 划界。除非您通常不设置Required属性 您需要覆盖另一个事务属性。因为 事务属性是声明性的,您可以轻松地更改它们 后面。
在这种情况下,我不需要在方法@TransactionAttribute(REQUIRED)
和Bean1Impl#method1()
中指定Bean2Impl#method2()
注释声明。我是对的吗?
因此,在上面的代码中,Bean2Impl#method2()
的事务将在Bean1Impl#method1()
的事务中运行。
我可以将其视为嵌套交易吗?
如果方法Exception
中存在Bean2Impl#method2()
,最终会导致从EJBContext.setRollbackOnly()
块调用方法catch
,并且正如预期的那样应该回滚在此方法的try
块内执行的操作。在这种情况下,交易会发生什么,以及Bean1Impl#method1()
。它会被回滚吗?我的意思是:
如果来自EJBContext.setRollbackOnly()
和
Bean2Impl#method2()
来电,会发生什么情况
Bean2Impl#method2()
调用Bean1Impl#method1()
。Bean2Impl#method2()
调用Bean1Impl#method1()
。最后,如果方法Bean2Impl#method2()
成功执行但EJBContext.setRollbackOnly()
成功返回Bean1Impl#method1()
后会从Bean2Impl#method2()
调用会发生什么?
答案 0 :(得分:4)
这不是嵌套事务,JavaEE / JTA不支持嵌套事务。如果从#method2()
调用#method1()
,则它在同一事务中运行。如果您想要进行其他交易,则需要#REQUIRES_NEW
。 EJBContext.setRollbackOnly()
仅适用于当前事务。注意,在调用EJBContext.setRollbackOnly()
之后,事务资源上的所有操作(包括读取)都会抛出异常(JBoss AS 5.1这样做,不知道当前的行为)。
<强>更新强>
}catch(Throwable th){
context.setRollbackOnly();
}
对于运行时异常,您不需要这样做,这是EJB的默认行为。
答案 1 :(得分:4)
要添加@Philippe Marshall的正确答案和您的评论 - REQUIRES_NEW
将创建一个独立于第一个的新事务。它们不是嵌套的。第一个事务暂停,而第二个事务处于活动状态。一旦第二个事务提交,第一个事务就会恢复。
您不必手动setRollbackOnly()
。如果需要,大多数PersistenceException
都会这样做。通过回滚您不必要的交易,您将获得任何收益。例如,在查询数据时,您可能会获得NoResultException
或NonUniqueResultException
。它们不会导致事务回滚,因为持久性上下文和数据库之间不存在不一致的风险。
您无需同时指定@TransactionAttribute(REQUIRED)
或@TransactionManagement(TransactionManagementType.CONTAINER)
- 两者都是默认设置。
编辑:回答你的进一步问题:
我假设@TransactionAttribute(REQUIRES_NEW)
method2
,因此两个单独的交易。
如果有Exception
导致在method2
中回滚事务,则如果捕获到异常,则不会回滚来自method1
的事务。如果未捕获Exception
,则将回滚这两个事务。
在事务上设置回滚标志时,无论是在DB操作之前还是之后都发生这种情况,因为整个事务都会回滚。
一旦method2
返回,它的事务就会被提交。事后从method1
回滚或提交交易对第一笔交易的结果没有影响。
一般建议 - 不要抓住Throwable
- 它太宽泛了,你可能会吞下你宁愿传播到表面的异常。