调用entityManager.getTransaction()时出现EJBException

8
这可能是一些琐碎的事情,但我需要帮助。 我得到:
 javax.ejb.EJBException: java.lang.IllegalStateException: Illegal to call this method from injected, managed EntityManager 
 11:54:37,105 ERROR [STDERR] at org.jboss.ejb3.tx.Ejb3TxPolicy.handleExceptionInOurTx(Ejb3TxPolicy.java:77)
 11:54:37,105 ERROR [STDERR] at org.jboss.aspects.tx.TxPolicy.invokeInOurTx(TxPolicy.java:83)
 11:54:37,105 ERROR [STDERR] at org.jboss.aspects.tx.TxInterceptor$Required.invoke(TxInterceptor.java:190)

当进行以下操作时:

@PersistenceContext(unitName = "someName")
private EntityManager em;

...

final EntityManager entityManager = getEntityManager();
final EntityTransaction tx = entityManager.getTransaction(); // here

有人能告诉我可能是什么原因吗?(涉及IT技术)
2个回答

19
在Java EE托管上下文中获取与EntityManager关联的EntityTransaction实例是非法的。从EntityManager.getTransaction()的Java EE API文档中可以了解到:

Return the resource-level EntityTransaction object. The EntityTransaction instance may be used serially to begin and commit multiple transactions.

Returns:
    EntityTransaction instance 
Throws:
    IllegalStateException - if invoked on a JTA entity manager

在这个上下文中,最后一行很重要。

当您在应用程序服务器上使用@PersistenceContext或@Inject注释将EntityManager注入EJB时,EntityManager将由容器管理而不是由应用程序管理。容器管理的实体管理器必须是JTA实体管理器;应用程序管理的实体管理器可以是资源本地实体管理器。这是由JPA规范决定的:

其基础事务通过JTA控制的实体管理器称为JTA实体管理器。

其基础事务通过EntityTransaction API由应用程序控制的实体管理器称为资源本地实体管理器。

容器管理的实体管理器必须是JTA实体管理器。 JTA实体管理器仅指定用于Java EE容器。

从第一个观点(关于IllegalStateException)推断,您不得获取容器注入的EntityManagers的EntityTransaction引用。但是,如果容器仅注入了EntityManagerFactory,并且您的应用程序通过调用EntityManagerFactory.getEntityManager获得了EntityManager引用,则可以这样做。

此外,应该注意,对于JTA实体管理器,调用EntityManager.getTransaction()是没有意义的。这在EntityTransaction接口的定义中由JPA规范指出:

EntityTransaction接口用于在资源本地实体管理器上控制资源事务。

关于管理JTA事务本身的话题,如果您需要自己管理事务边界(即使用bean-managed transactions),则注入UserTransaction实例。或者,如果您希望容器管理事务,则只需用适当的TransactionalAttribute值注释方法或bean即可。
在应用服务器中,通常不建议在bean managed或container managed transactions中使用resource-local entity managers(和数据源),但是可以这样做。
您将在Hibernate EntityManager documentation中找到一个合适的示例,演示了如何在注入EntityManager的情况下使用BMTs。如果您已经对bean类或方法进行了注释,则CMTs甚至更加简单;您只需避免调用getEntityTransaction()方法即可使CMTs工作。
如果您想进一步了解,我建议阅读JPA 2.0规范的第7章,标题为“实体管理器和持久性上下文”。该章提供的示例演示了:
JTA实体管理器在应用服务器中的使用方式(通常是它们的使用地点)。
资源本地实体管理器如何在应用服务器中使用。
资源本地实体管理器如何在Java SE应用程序中使用。

非常感谢您详尽的回答。 - Simeon
@Simeon,不客气。不幸的是,我的原始答案在一些地方存在歧义,可能被错误地解释了。我已经更新了它,包括可以使用JTA和资源本地实体管理器的位置以及因此对EntityManager.getTransaction()方法适用的限制。现在应该更清楚了。 - Vineet Reynolds
没有问题,这解决了我的疑惑,再次感谢。我实际上将这个答案存储起来以备后用 :) 我使用@TransationAttribute做到了我想要的,但现在我明白为什么entityManager.getTransaction();不起作用了。 - Simeon

3

你不需要手动实例化EntityManager对象,因为你的容器会根据@PersistenceContext注解自动进行实例化。此外,你也不需要手动开始事务,容器也会为你提供。只需使用em字段即可,无需关心其他内容。


1
我正在尝试持久化一个Set<Entity>,并且希望有类似tx.begin(); // 迭代并持久化set; tx.commit();的东西。是否有一种不需要用户管理事务的方法来实现这一点? - Simeon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接