【问题标题】:Transaction across EJB and CDI beans跨 EJB 和 CDI bean 的事务
【发布时间】:2021-07-14 11:19:54
【问题描述】:

我们继承了一个同时使用 EJB 和 CDI 的应用程序,例如 EJB 调用 CDI bean 的方法。

当从 EJB 调用 CDI bean 时,我仍然不确定事务管理/处理是如何工作的。

让我们假设以下简单示例:

@Stateless
public class MyEjb {

    @Inject
    private MyCdi myCdi;
    
    public void doMyEjbStuff() {
        myCdi.doMyCdiStuff();
    }
}

@ApplicationScoped
public class MyCdi {

    public void doMyCdiStuff() {
        // some database operations ...
        throw new RuntimeException("Error");
    }
}

现在doMyEjbStuff() 在事务上下文之外被调用,所以当它被调用时会创建一个新事务。

在这种情况下,我希望在调用doMyEjbStuff() 时创建的事务跨越对doMyCdiStuff() 的调用,并由于异常而回滚。日志应显示javax.ejb.EJBException: Transaction aborted 消息。这是正确的吗?

如果我用@Transactional 注释doMyCdiStuff() 会有什么改变吗?

如果我用@Transactional(Transactional.TxType.REQUIRES_NEW) 注释doMyCdiStuff() 会发生什么?我猜现在抛出了TransactionalException。这个异常会导致调用doMyEjbStuff()启动的事务回滚吗?

【问题讨论】:

    标签: jakarta-ee transactions ejb cdi


    【解决方案1】:

    我会根据我的经验回答,但您也应该尝试一下来验证一下。总的来说,我认为你做对了大部分事情。

    首先,在JEE/JakartaEE 应用服务器中,支持EJB 事务和CDI 事务的事务机制是相同的。事实上,CDI @Transactional 注解是在 JTA 规范中指定的。

    默认情况下,EJB 启动或加入事务,因此当调用MyEjb.doMyEjbStuff() 时,它将始终在事务中运行。此事务将跨越到MyCdi.doMyCdiStuff() 方法。

    日志应显示 javax.ejb.EJBException: Transaction aborted 消息。

    我不确定日志会显示什么,这甚至可能是特定于实现的。但是,只要RuntimeException 命中 EJB 事务拦截器,事务回滚。 RuntimeException 不是应用程序异常,因此它会导致事务回滚并且容器抛出一个包装它的EJBException。 (EJB 3.2 规范,第 9.2.2 章)

    如果我用 @Transactional 注释 doMyCdiStuff() 会有什么改变吗?

    @Transactional 注释的 JTA 规范说 RuntimeException 将标记事务以进行回滚(JTA 1.2 规范,ch.3.7)。所以当前的交易。即,如果RuntimeException 命中事务拦截器,则由 EJB 启动的那个将被标记为回滚。由于没有捕获异常,它将从 JTA/CDI 事务拦截器传递,后者将标记事务以进行回滚,然后到达 EJB 事务拦截器(或等效的),该拦截器也将标记事务以进行回滚。所以在这种情况下没有任何变化。

    如果 EJB 真的捕获了 RuntimeException 并在它之后做了一些事情,那将会有一个细微的差别:没有 JTA/CDI @Transactional 异常永远不会到达容器,因此“某事”将被调用并且整体交易可能成功。如果@Transactional 存在,则当前事务无论如何都会被标记为回滚。 catch(RuntimeException e) 之后的东西将运行并看似成功完成,但整体工作将回滚。当心这种情况,如果“某事”有副作用!

    如果我用@Transactional(Transactional.TxType.REQUIRES_NEW) 注释doMyCdiStuff() 会发生什么?

    根据上面的讨论,当前事务,即由@Transactional(REQUIRES_NEW) 启动的事务将被回滚。但是没有人捕捉到异常,因此它传播到 EJB 并回滚该事务。并被抛出,包裹在EJBException 中。如果 EJB 捕获到异常,则只会回滚内部事务。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-12
      • 2014-06-05
      • 2017-08-17
      • 2013-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多