让我们考虑两个方法methodA()
注释@TransactionAttribute(TransactionAttributeType.REQUIRED)
(默认)和methodB()
在无状态EJB中注释{/ 1}}。
通过@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
对methodB()
进行嵌套调用不会启动/创建新事务(因为目标方法使用的是什么事务属性类型),因为嵌套调用从methodA()
到methodB()
使用methodA()
指针/引用(即实际的EJB实例)来调用this
,因此不会被要注入的代理拦截容器运行时需要在调用方法之前设置环境。
基本演示:
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()
获取的代理实例调用getRowCount()
方法,则会抛出异常。以下代码段演示了此修改。
javax.ejb.SessionContext
由于@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()
指针 - 实际的EJB实例)。
通过this
获取代理实例并在该代理实例上调用方法是一种破解。我认为不应该在实际应用中使用它。
是否有其他方法可以在需要时在嵌套方法调用中启动/创建新事务?
答案 0 :(得分:1)
仅当您通过接口/代理访问bean而不是内部方法时,才会遵守事务属性,如您所观察到的那样。因此,除了通过SessionContext
访问它之外,您还可以在班级中使用@EJB TestBeanService serviceBean
并通过serviceBean.getRowCount()
访问它(也是一种黑客攻击)。