我们继承了一个同时使用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捕捉到异常,则只会回滚内部事务。