容器管理的持久性上下文
当使用容器管理的持久性上下文时(就像通过@PersistenceContext注释一样),JPA规范指定仅一个持久性上下文可以与JTA事务关联。
持久性上下文由Java
EE容器创建。尽管出现了代码(@PersistenceContext批注似乎表明PC已直接注入您的EntityManager实例变量中),但持久性上下文实际上是作为引用存储在JTA事务中的。每次执行EntityManager操作时,它都不会引用它自己的内部持久性上下文。相反,它进行了特殊的操作,因为它是容器管理的-
它总是在JTA Transaction中查找持久性上下文并使用它。这称为JTA持久性上下文传播。
JPA规范的一些引言:
使用容器管理的实体管理器时,始终自动对应用程序透明地管理持久性上下文的生命周期,并且持久性上下文随JTA事务传播。
容器管理的事务作用域持久性上下文
… 当在活动的JTA事务范围内调用容器管理的实体管理器[76]时,新的持久性上下文开始,并且当前不存在与JTA事务相关联的持久性上下文。
创建持久性上下文,然后将其与JTA事务关联。容器管理的扩展持久性上下文
…容器管理的扩展持久性上下文只能在有状态会话bean的范围内启动。从创建声明对PersistenceContextType.EXTENDED类型的实体管理器具有依赖性的有状态会话Bean的点开始存在,并说它已绑定到有状态会话Bean。通过PersistenceContext批注或persistence-
context-
ref部署描述符元素声明对扩展的持久性上下文的依赖关系。当有状态会话Bean的@Remove方法完成时(否则,有状态会话Bean实例被破坏),容器将关闭持久性上下文。持久性上下文传播的要求
…如果调用了一个组件并且没有JTA事务…,则不会传播持久性上下文。•调用用PersistenceContextType.TRANSACTION定义的实体管理器将导致使用新的持久性上下文。•调用用PersistenceContextType.EXTENDED定义的实体管理器将导致使用绑定到该组件的现有扩展持久性上下文。
…如果调用了某个组件并将JTA事务传播到该组件中:•如果该组件是有状态会话Bean,扩展的持久性上下文已绑定到该状态Bean,并且JTA事务绑定了另一个持久性上下文,容器抛出EJBException。•否则,如果存在绑定到JTA事务的持久性上下文,则将传播和使用该持久性上下文。
那就是你的问题。 一个明显的$ 64问题:为什么规范要求这样做?
嗯,这是因为这是一个有意的折衷,将强大的EntityManager魔术带给EJB。
使用JTA事务传播单个持久性上下文有一个局限性:事务不能跨越多个持久性上下文,因此不能跨越多个数据库。
但是,它还有一个巨大的优势:在EJB中声明的任何entityManager都可以自动共享相同的持久性上下文,因此可以在同一组JPA实体上运行并参与同一事务。您可以有一串EJB,它们调用其他任何复杂程度的EJB,并且它们都针对JPA实体数据合理且一致地运行。而且,它们也不需要跨方法调用一致地初始化/共享实体管理器引用的复杂性-
EntityManagers可以在每个方法中私有地声明。实现逻辑可以非常简单。
您问题的答案:使用应用程序管理的持久性上下文(通过应用程序管理的EntityManagers )
通过以下方法之一声明您的entityManager:
// "Java EE style" declaration of EM@PersistenceUnit(unitName="H2PU")EntityManagerFactory emfH2;EntityManager emH2 = emfH2.createEntityManager();
要么
// "JSE style" declaration of EMEntityManagerFactory emfH2 = javax.persistence.Persistence.createEntityManagerFactory("H2PU");EntityManager emH2 = emfH2.createEntityManager();and the same for emfOracle & emOracle.完成每个EM时,必须调用em.close()-最好通过final {}子句或Java 7 try-with-resources语句。
应用程序管理的EM仍参与(即与之同步)JTA事务。任何数量的应用程序管理的EM都可以参与单个JTA事务-但是这些持久性上下文都不会与任何 容器管理的EM
相关联或传播到任何 容器管理的EM 。
如果EntityManager是在JTA事务的上下文之外创建的(在事务开始之前),则必须明确要求它加入JTA事务:
// must be run from within Java EE pre scope that already has a JTA // transaction active:em.joinTransaction();
甚至更简单,如果EntityManager是在JTA事务的上下文内创建的,则由应用程序管理的EntityManager会自动隐式地加入JTA事务-
无需joinTransaction()。
因此,应用程序管理的EM可以具有跨多个数据库的JTA事务。当然,您始终可以独立于JTA运行本地资源JDBC事务:
EntityTransaction tx = em.getTransaction(); tx.begin();// ....tx.commit();
编辑:使用应用程序管理的实体管理器进行事务管理的更多详细信息
警告:以下代码示例仅供教育用途-我将其打在脑海中以帮助解释我的观点,并且没有时间进行编译/调试/测试。
EJB的默认@TransactionManagement参数是TransactionManagement.CONTAINER,EJB方法的默认@TransactionAttribute参数是TransactionAttribute.REQUIRED。
事务管理有四个排列:
- A)具有CONTAINER管理的JTA事务的EJB
这是首选的Java EE方法。
EJB类@TransactionManagement批注:
必须显式设置为TransactionManagement.CONTAINER或将其忽略以隐式使用默认值。
EJB方法@TransactionAttribute批注:必须显式设置为TransactionAttribute.REQUIRED或将其忽略以隐式使用默认值。(注意:如果您有不同的业务场景,则可以使用TransactionAttribute.MANDATORY或TransactionAttribute.REQUIRES_NEW,如果它们的语义符合您的需求。)
应用程序管理的实体管理器:
必须通过Persistence.createEntityManagerFactory(“
unitName”)和emf创建它们.createEntityManager(),如上所述。
通过JTA事务加入EntityManagers:
在事务性EJB方法中创建EntityManager,它们将自动加入JTA事务。或者,如果预先创建了EntityManager,则在事务EJB方法中调用em.joinTransaction()。
使用完后,请调用EntityManager.close()。那应该是所有需要的。
基本示例-只需使用更多EntityManager即可在多个DB之间进行事务处理:
@Stateless public class EmployeeServiceBean implements EmployeeService { // Transactional method public void createEmployee() { EntityManagerFactory emf = Persistence.createEntityManagerFactory("EmployeeService"); EntityManager em = emf.createEntityManager(); Employee emp = ...; // set some data // No need for manual join - em created in active tx context, automatic join: // em.joinTransaction(); em.persist(emp); // other data & em operations ... // call other EJBs to partake in same transaction ... em.close(); // Note: em can be closed before JTA tx committed. // Persistence Context will still exist & be propagated // within JTA tx. Another EM instance could be declared and it // would propagate & associate the persistence context to it. // Some time later when tx is committed [at end of this // method], Data will still be flushed and committed and // Persistence Context removed . emf.close(); }}@Stateful public class EmployeeServiceBean implements EmployeeService { // Because bean is stateful, can store as instance vars and use in multiple methods private EntityManagerFactory emf; private EntityManager em; @PostConstruct // automatically called when EJB constructed and session starts public void init() { emf = Persistence.createEntityManagerFactory("EmployeeService"); em = emf.createEntityManager(); } // Transactional method public void createEmployee() { Employee emp = ...; // set some data em.joinTransaction(); // em created before JTA tx - manual join em.persist(emp); } // Transactional method public void updateEmployee() { Employee emp = em.find(...); // load the employee // don't do join if both methods called in same session - can only call once: // em.joinTransaction(); // em created before JTA tx - manual join emp.set(...); // change some data // no persist call - automatically flushed with commit } @Remove // automatically called when EJB session ends public void cleanup() { em.close(); emf.close(); }// ...}- B)具有BEAN管理的JTA事务的EJB
使用@ TransactionManagement.BEAN。
注入JTA UserTransaction接口,以便Bean可以直接标记JTA事务。
通过UserTransaction.begin()/ commit()/ rollback()手动标记/同步事务。
确保EntityManager加入JTA事务-在活动的JTA事务上下文中创建EM或调用em.joinTransaction()。
例子:
@TransactionManagement(TransactionManagement.BEAN) @Stateless public class EmployeeServiceBean implements EmployeeService { // inject the JTA transaction interface @Resource UserTransaction jtaTx; public void createEmployee() { EntityManagerFactory emf = Persistence.createEntityManagerFactory("EmployeeService"); EntityManager em = emf.createEntityManager(); try { jtaTx.begin(); try { em.joinTransaction(); Employee emp = ...; // set some data em.persist(emp); // other data & em operations ... // call other EJBs to partake in same transaction ... } finally { jtaTx.commit(); } } catch (Exception e) {// handle exceptions from UserTransaction methods// ... } Employee emp = ...; // set some data // No need for manual join - em created in active tx context, automatic join: // em.joinTransaction(); em.persist(emp); em.close(); // Note: em can be closed before JTA tx committed. // Persistence Context will still exist inside JTA tx. // Data will still be flushed and committed and Persistence // Context removed some time later when tx is committed. emf.close(); }}- C)POJO / Non-EJB,带有手工编码(bean管理的)资源本地事务(不是JTA)
只需使用JPA EntityTransaction接口进行tx划分(通过em.getTransaction()获得)。
例:
public class ProjectServlet extends HttpServlet { @EJB ProjectService bean; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // ... try { EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPU"); EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); try { bean.assignEmployeeToProject(projectId, empId); bean.updateProjectStatistics(); } finally { tx.commit(); } } catch (Exception e) {// handle exceptions from EntityTransaction methods// ... } // ... }}- D)具有手动编码(POJO管理的)JTA事务的POJO / Non-EJB
假设POJO /组件在具有JTA支持的某个容器中运行。
如果在Java EE容器中,可以使用JTA UserTransaction接口的Java EE资源注入。
(或者,可以显式地查找JTA接口的句柄并对其进行划分,然后调用em.getTransaction()。joinTransaction()-参见JTA规范。)
例:
public class ProjectServlet extends HttpServlet { @Resource UserTransaction tx; @EJB ProjectService bean; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // ... try { tx.begin(); try { bean.assignEmployeeToProject(projectId, empId); bean.updateProjectStatistics(); EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPU"); EntityManager em = emf.createEntityManager(); // Should be able to avoid explicit call to join transaction. // Should automatically join because EM created in active tx context. // em.joinTransaction(); // em operations on data here em.close(); emf.close(); } finally { tx.commit(); } } catch (Exception e) {// handle exceptions from UserTransaction methods// ... } // ... }}


