【发布时间】:2015-08-28 05:54:01
【问题描述】:
让我们考虑在无状态 EJB 中使用 @TransactionAttribute(TransactionAttributeType.REQUIRED)(默认)注释的两种方法 methodA() 和使用 @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 注释的 methodB()。
通过methodA() 对methodB() 进行嵌套调用不会启动/创建新事务,因为它看起来是(无论目标方法使用什么事务属性类型),因为对methodB() 的嵌套调用来自methodA() 使用this 指针/引用(即实际的EJB 实例)来调用methodB(),因此不会被要在运行时由容器注入并需要设置环境的代理拦截在调用方法之前。
基本演示:
@Stateless
public class TestBean implements TestBeanService {
@PersistenceContext
private EntityManager entityManager;
// At a glance, this method should cause an exception but it does not.
@Override
@TransactionAttribute(TransactionAttributeType.NEVER)
public Long getRowCount() {
return entityManager.createQuery("SELECT count(e) AS cnt FROM Employee e", Long.class).getSingleResult();
}
// This method is invoked by the application client.
// making a nested call to getRowCount() using the "this" pointer.
@Override
public void test() {
Long rowCount = getRowCount();
System.out.println("rowCount : " + rowCount);
}
}
虽然getRowCount()方法被@TransactionAttribute(TransactionAttributeType.NEVER)修饰了一眼应该会导致异常,但它成功返回了查询返回的行数。
► 这是因为test() 方法启动的事务被传播(扩展)到getRowCount(),即所有事情都发生在同一个事务中。
但是,如果使用通过javax.ejb.SessionContext 获得的代理实例来调用getRowCount() 方法,则会引发异常。下面的 sn-p 演示了这种修改。
@Stateless
public class TestBean implements TestBeanService {
@PersistenceContext
private EntityManager entityManager;
@Resource
private SessionContext sessionContext;
@Override
@TransactionAttribute(TransactionAttributeType.NEVER)
public Long getRowCount() {
return entityManager.createQuery("SELECT count(e) AS cnt FROM Employee e", Long.class).getSingleResult();
}
@Override
public void test() {
// Invocation to getRowCount() is done by a proxy instance. Hence, it causes an exception,
// since the transaction started by this method is now not propagated to a subsequent call to getRowCount().
Long rowCount = sessionContext.getBusinessObject(TestBeanService.class).getRowCount();
System.out.println("rowCount : " + rowCount);
}
}
由于getRowCount() 方法使用TransactionAttributeType.NEVER,因此上述对getRowCount() 的方法调用会引发以下与第一种情况相矛盾的异常。
javax.ejb.EJBException: EJB cannot be invoked in global transaction
► 这是因为由test() 方法启动的事务现在不传播(扩展)到getRowCount(),就像第一种情况一样,因为现在通过代理实例调用此方法(因此,不像往常那样直接通过 this 指针 - 实际的 EJB 实例)。
通过javax.ejb.SessionContext 获取代理实例并在该代理实例上调用方法是一种 hack。我认为它不应该在实际应用中使用。
是否有任何其他方法可以在需要时在嵌套方法调用中启动/创建新事务?
【问题讨论】:
标签: jakarta-ee transactions ejb