在同一个 EJB 中调用方法时的事务传播
Transaction propagation when calling methods inside the same EJB
我有一个简单的 EJB 无状态 class,它有两个方法,都没有 @TransactionAttribute。第一个方法保存啤酒并调用另一个保存其他啤酒并抛出异常的方法。
@Stateless
public class EJBContainer {
@PersistenceContext
EntityManager em;
public void testTransaction() {
em.persist(getBeer("Corona"));
try {
saveAndThrowException();
} catch (Exception e) {
e.printStackTrace();
}
}
public void saveAndThrowException() {
em.persist(getBeer("Heineken"));
throw new RuntimeException();
}
//getBeer() definition
}
我假设这两种方法都将使用默认交易类型(必需)- 因此将在第一种方法中创建一个新交易,并将在第二种方法中使用它。因此抛出异常应该回滚此事务,并且 EntityManager 不会保存任何啤酒。但事实证明,两瓶啤酒都得救了。我哪里错了?
当我将第二个方法移动到另一个无状态 EJB 时,它按我预期的那样工作 - 第一个 EJB 创建了新事务,第二个使用了它,异常导致回滚并且没有保存任何啤酒。
从这个 JAX-RS 客户端调用 EJB:
@ApplicationScoped
public class BeerController {
@Inject
private EJBContainer ejbContainer;
@POST
public void addBeer() {
ejbContainer.testTransaction();
}
}
由于您使用的是无界面视图,注入可能无法正常工作。我会尝试使用 @EJB
注释而不是 @Inject
.
@EJB
private EJBContainer ejbContainer;
@POST
public void addBeer() {
ejbContainer.testTransaction();
}
这样你应该会得到预期的结果。但是您的假设之一并不完全正确。当您从 testTransacion
调用 saveAndThrowException
不是 EJB 调用时,它只是本地方法调用,如果您为 saveAndThrowException
声明了不同的 @TransactionAttribute
if 则无关紧要。
如果您需要从 EJB 的另一个事务方法调用事务方法,您可以添加对 EJB 本身的引用,然后像调用另一个 EJB 一样调用它:
@Stateless
public class EJBContainer {
@PersistenceContext
EntityManager em;
@EJB
private EJBContainer ejbContainer;
public void testTransaction() {
em.persist(getBeer("Corona"));
try {
ejbContainer.saveAndThrowException();
} catch (Exception e) {
e.printStackTrace();
}
}
public void saveAndThrowException() {
em.persist(getBeer("Heineken"));
throw new RuntimeException();
}
//getBeer() definition
}
还不能发表评论,声望不够。因此单独 post.
阿瑞斯说的有道理。您调用它的方式只是本地调用,因此应用程序服务器省略了从外部调用它时执行的所有围绕 saveAndThrowException 的拦截器。您抛出异常并在将控制权返回给应用程序服务器之前直接捕获它。应用程序服务器对异常一无所知,因此一切正常,事务已提交。
我有一个简单的 EJB 无状态 class,它有两个方法,都没有 @TransactionAttribute。第一个方法保存啤酒并调用另一个保存其他啤酒并抛出异常的方法。
@Stateless
public class EJBContainer {
@PersistenceContext
EntityManager em;
public void testTransaction() {
em.persist(getBeer("Corona"));
try {
saveAndThrowException();
} catch (Exception e) {
e.printStackTrace();
}
}
public void saveAndThrowException() {
em.persist(getBeer("Heineken"));
throw new RuntimeException();
}
//getBeer() definition
}
我假设这两种方法都将使用默认交易类型(必需)- 因此将在第一种方法中创建一个新交易,并将在第二种方法中使用它。因此抛出异常应该回滚此事务,并且 EntityManager 不会保存任何啤酒。但事实证明,两瓶啤酒都得救了。我哪里错了?
当我将第二个方法移动到另一个无状态 EJB 时,它按我预期的那样工作 - 第一个 EJB 创建了新事务,第二个使用了它,异常导致回滚并且没有保存任何啤酒。
从这个 JAX-RS 客户端调用 EJB:
@ApplicationScoped
public class BeerController {
@Inject
private EJBContainer ejbContainer;
@POST
public void addBeer() {
ejbContainer.testTransaction();
}
}
由于您使用的是无界面视图,注入可能无法正常工作。我会尝试使用 @EJB
注释而不是 @Inject
.
@EJB
private EJBContainer ejbContainer;
@POST
public void addBeer() {
ejbContainer.testTransaction();
}
这样你应该会得到预期的结果。但是您的假设之一并不完全正确。当您从 testTransacion
调用 saveAndThrowException
不是 EJB 调用时,它只是本地方法调用,如果您为 saveAndThrowException
声明了不同的 @TransactionAttribute
if 则无关紧要。
如果您需要从 EJB 的另一个事务方法调用事务方法,您可以添加对 EJB 本身的引用,然后像调用另一个 EJB 一样调用它:
@Stateless
public class EJBContainer {
@PersistenceContext
EntityManager em;
@EJB
private EJBContainer ejbContainer;
public void testTransaction() {
em.persist(getBeer("Corona"));
try {
ejbContainer.saveAndThrowException();
} catch (Exception e) {
e.printStackTrace();
}
}
public void saveAndThrowException() {
em.persist(getBeer("Heineken"));
throw new RuntimeException();
}
//getBeer() definition
}
还不能发表评论,声望不够。因此单独 post.
阿瑞斯说的有道理。您调用它的方式只是本地调用,因此应用程序服务器省略了从外部调用它时执行的所有围绕 saveAndThrowException 的拦截器。您抛出异常并在将控制权返回给应用程序服务器之前直接捕获它。应用程序服务器对异常一无所知,因此一切正常,事务已提交。