在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应用程序中使用。
EntityManager.getTransaction()
方法适用的限制。现在应该更清楚了。 - Vineet Reynolds