EJB和CDI bean之间的事务



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

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

让我们假设以下简单的例子:

@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()启动的事务回滚?

我会根据我的经验回答,但您可能也应该进行一些实验来验证。总的来说,我认为你大多数事情都做对了。

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

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

日志应该显示javax.ejb.EJBException:事务中止消息。

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

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

@Transactional注释的JTA规范说RuntimeException将标记事务进行回滚(JTA 1.2规范,ch.3.7(。即如果CCD_ 21命中事务拦截器,则由EJB启动的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捕捉到异常,则只会回滚内部事务。

最新更新