嵌套事务和EJBContext的setRollbackOnly()

时间:2013-12-29 09:34:18

标签: java java-ee jta java-ee-7 ejb-3.2

我正在阅读Java EE 7的事务管理,我对嵌套事务的概念和EJBContext#setRollbackOnly()的功能感到困惑。

假设我有两个会话Bean,Bean1ImplBean2Impl并且他们的签名是:

@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()
  • 在执行任何数据库操作(如persist,merge或remove)之后,从方法Bean2Impl#method2()调用
  • Bean1Impl#method1()

最后,如果方法Bean2Impl#method2()成功执行但EJBContext.setRollbackOnly()成功返回Bean1Impl#method1()后会从Bean2Impl#method2()调用会发生什么?

2 个答案:

答案 0 :(得分:4)

这不是嵌套事务,JavaEE / JTA不支持嵌套事务。如果从#method2()调用#method1(),则它在同一事务中运行。如果您想要进行其他交易,则需要#REQUIRES_NEWEJBContext.setRollbackOnly()仅适用于当前事务。注意,在调用EJBContext.setRollbackOnly()之后,事务资源上的所有操作(包括读取)都会抛出异常(JBoss AS 5.1这样做,不知道当前的行为)。

<强>更新

    }catch(Throwable th){
        context.setRollbackOnly();
    }

对于运行时异常,您不需要这样做,这是EJB的默认行为。

答案 1 :(得分:4)

要添加@Philippe Marshall的正确答案和您的评论 - REQUIRES_NEW将创建一个独立于第一个的新事务。它们不是嵌套的。第一个事务暂停,而第二个事务处于活动状态。一旦第二个事务提交,第一个事务就会恢复。

您不必手动setRollbackOnly()。如果需要,大多数PersistenceException都会这样做。通过回滚您不必要的交易,您将获得任何收益。例如,在查询数据时,您可能会获得NoResultExceptionNonUniqueResultException。它们不会导致事务回滚,因为持久性上下文和数据库之间不存在不一致的风险。

您无需同时指定@TransactionAttribute(REQUIRED)@TransactionManagement(TransactionManagementType.CONTAINER) - 两者都是默认设置。

编辑:回答你的进一步问题:

我假设@TransactionAttribute(REQUIRES_NEW) method2,因此两个单独的交易。

如果有Exception导致在method2中回滚事务,则如果捕获到异常,则不会回滚来自method1的事务。如果未捕获Exception,则将回滚这两个事务。

在事务上设置回滚标志时,无论是在DB操作之前还是之后都发生这种情况,因为整个事务都会回滚。

一旦method2返回,它的事务就会被提交。事后从method1回滚或提交交易对第一笔交易的结果没有影响。

一般建议 - 不要抓住Throwable - 它太宽泛了,你可能会吞下你宁愿传播到表面的异常。