为什么具有分离数据源的不同持久性单元会查询相同的数据源? [英] Why different persistence units with separated data sources query the same data source?

查看:96
本文介绍了为什么具有分离数据源的不同持久性单元会查询相同的数据源?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个需要访问两个不同数据库服务器(H2和Oracle)的webapp。
容器是 Apache Tomee 1.5.1 ,我正在使用带有库的Java EE堆栈提供它(JSF,JPA,CDI,EJB等)。



我正在尝试在XA事务中使用两个实体管理器从Oracle提取数据数据库并在转换后将其保留在H2中,但无论我使用的实体管理器如何,所有查询都是针对H2数据库执行的。有帮助吗?



编辑:我发现如果我尝试按逆序访问实体管理器,它们的行为是相同的,但访问Oracle。即:实体经理留在第一个访问的数据库。



发生这种情况的EJB(调用 service.getFoo()来自JSF):

  @Named 
@Stateless
公共类服务{
@Inject
@OracleDatabase
private EntityManager emOracle;

@Inject
@ H2Database
private EntityManager emH2;

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public List< Foo> getFoo(){
TypedQuery< Foo> q = emH2.createQuery(
SELECT x FROM Foo f,Foo.class);
列表< Foo> l = q.getResultList();
if(l == null || l.isEmpty()){
update();
}

返回q.getResultList();
}

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void update(){
// FAIL:此查询使用Oracle实体管理器对H2执行!
List< Object []> l = emOracle.createNativeQuery(SELECT * FROM bar)。getResultList();

//更多东西......
}
}

实体管理器的资源生成器(CDI)(其中@ H2Database和@OracleDatabase是限定符):

 公共类资源{
@Produces
@PersistenceContext( unitName =OraclePU)
@OracleDatabase
private EntityManager emOracle;

@Produces
@PersistenceContext(unitName =H2PU)
@ H2Database
private EntityManager emH2;
}

我的 peristence.xml 如下所示:

 <?xml version =1.0encoding =UTF-8?> 
< persistence version =2.0
xmlns =http://java.sun.com/xml/ns/persistencexmlns:xsi =http://www.w3.org/ 2001 / XMLSchema-instance
xsi:schemaLocation =http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd >
< persistence-unit name =H2PU
transaction-type =JTA>
< provider> org.apache.openjpa.persistence.PersistenceProviderImpl< / provider>
< jta-data-source> H2DS< / jta-data-source>
< class> my.app.h2.Foo< / class>
< exclude-unlisted-classes> true< / exclude-unlisted-classes>
< / persistence-unit>

< persistence-unit name =OraclePUtransaction-type =JTA>
< provider> org.apache.openjpa.persistence.PersistenceProviderImpl< / provider>
< jta-data-source> OracleDS< / jta-data-source>
< class> my.app.oracle.Bar< / class>
< exclude-unlisted-classes> true< / exclude-unlisted-classes>
< / persistence-unit>
< / persistence>

最后, tomee.xml 中的数据源(还有' t此文件中配置的任何其他数据源):

 < Resource id =OracleDStype =javax.sql。数据源> 
jdbcDriver = oracle.jdbc.xa.client.OracleXADataSource
jdbcUrl = jdbc:oracle:thin:@server:port:instance
jtaManaged = true
password = abcde
userName = user
< / Resource>

< Resource id =H2DStype =javax.sql.DataSource>
jdbcDriver = org.h2.jdbcx.JdbcDataSource
jdbcUrl = jdbc:h2:h2 / db; AUTO_SERVER = TRUE
jtaManaged = true
password = edcba
userName =用户
< /资源>


解决方案

容器管理持久性上下文



当使用容器管理的持久性上下文时(因为您通过@PersistenceContext注释),JPA规范指定只有一个持久性上下文可能与JTA事务关联。



持久性上下文由Java EE容器创建。尽管出现了代码(@PersistenceContext注释似乎表明PC直接注入到您的EntityManager实例变量中),但持久化上下文实际上存储为JTA TRANSACTION中的引用。每次执行EntityManager操作时,它都不会引用它自己的内部持久性上下文。相反,它执行特殊操作,因为它是容器管理的 - 它总是查找JTA事务中的持久性上下文并使用它。这称为JTA持久化上下文传播。



JPA规范中的一些行情:


当使用容器管理的实体管理器时,
持久化上下文的生命周期总是自动管理,透明地管理应用程序
,持久化上下文使用$ b $传播b JTA事务。



容器管理的事务范围持久化上下文



...
当在活动JTA事务的范围内调用容器管理的实体管理器
时,新的持久化上下文开始,并且有
没有当前的持久化上下文已经与JTA
交易。创建持久化上下文,然后将
与JTA事务关联。



容器管理的扩展持久化上下文



...
容器管理的扩展持久化上下文只能在有状态会话bean的范围
内启动。它存在于有状态会话bean
声明对PersistenceContextType.EXTENDED
类型的实体管理器的依赖关系时,并且被称为绑定到有状态会话bean。通过PersistenceContext批注或persistence-context-ref部署描述符元素声明对
扩展持久性上下文的依赖性。
当有状态会话bean的@Remove方法完成时(或者有状态会话bean实例被破坏),容器关闭持久化上下文。



持久性上下文传播的要求



...如果调用组件且没有JTA事务...,则不传播持久性上下文。
•调用使用PersistenceContext定义的实体管理器 -
Type.TRANSACTION将导致使用新的持久化上下文。
•使用PersistenceContext-
Type.EXTENDED定义的实体管理器的调用将导致使用绑定到该组件的现有扩展持久化上下文



...如果调用了一个组件并且JTA事务传播到该组件中:
•如果该组件是一个有状态会话bean,扩展的持久化上下文已绑定到该有状态会话bean并且存在绑定到JTA事务的不同持久性上下文,容器抛出EJBException。
•否则,如果存在绑定到JTA事务的持久性上下文,则会传播和使用该持久性上下文。


这就是你的问题。
显而易见的64美元问题:为什么规范要求这个???



嗯,这是因为这是一次故意交易 - off为EJB带来了强大的EntityManager魔力。



使用JTA事务传播单个持久化上下文有一个限制:事务不能跨越多个持久化上下文,因此可以跨越多个数据库。



但是,它也有一个巨大的优势:在EJB中声明的任何entityManager都可以自动共享相同的持久化上下文,因此可以在同一组JPA实体上运行并参与同一笔交易。您可以让一系列EJB调用任何复杂的其他EJB,并且它们对JPA实体数据的行为都是合理且一致的。并且它们也不需要跨方法调用一致初始化/共享实体管理器引用的复杂性 - 可以在每个方法中私有地声明EntityManagers。实现逻辑可以非常简单。



您的问题的答案:使用应用程序管理的持久性上下文(通过应用程序管理的EntityManagers



通过以下方法之一声明您的entityManager:

  // Java EE样式声明EM 
@PersistenceUnit(unitName =H2PU)
EntityManagerFactory emfH2;
EntityManager emH2 = emfH2.createEntityManager();

OR

  // EM 
的JSE样式声明EntityManagerFactory emfH2 = javax.persistence.Persistence.createEntityManagerFactory(H2PU);
EntityManager emH2 = emfH2.createEntityManager();

和emfOracle& emOracle。

当你完成每个EM时,你必须调用em.close() - 优选通过final { }或通过Java 7 try-with-resources语句。



应用程序管理的EM仍然参与(即同步)JTA事务。任意数量的应用程序管理的EM都可以参与单个JTA事务 - 但是这些事务中的任何一个都不会将其持久性上下文与任何容器管理的EM 关联或传播。



如果EntityManager是在JTA交易的上下文之外创建的(在交易开始之前),那么你必须要求明确加入JTA交易:

  //必须从已经有JTA 
//事务活动的Java EE代码范围内运行:
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。需要。



交易管理有四种排列方式:




  • 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:

    创建EntityManagers WITHIN a事务EJB方法,它们将自动加入JTA事务。或者,如果事先创建了EntityManagers,则在事务EJB方法中调用em.joinTransaction()。

    完成使用后调用EntityManager.close()。
    这应该是所有必需的。



    基本示例 - 只需在多个数据库中使用更多EntityManagers进行交易:

      @Stateless 
    公共类EmployeeServiceBean实现EmployeeService {

    //事务方法
    public void createEmployee(){
    EntityManagerFactory emf = Persistence.createEntityManagerFactory(EmployeeService );
    EntityManager em = emf.createEntityManager();
    员工emp = ...; //设置一些数据
    //不需要手动连接 - 在活动tx上下文中创建的em,自动连接:
    // em.joinTransaction();
    em.persist(emp);
    //其他数据& em操作...
    //调用其他EJB参与同一事务...
    em.close(); //注意:在提交JTA tx之前可以关闭em。
    //持久性上下文仍然存在&在JTA tx中传播
    //。可以声明另一个EM实例,
    //将传播&将持久化上下文与之关联起来。
    //一段时间后tx被提交[在这个
    //方法的末尾],数据仍将被刷新并提交,
    //删除持久性上下文。
    emf.close();
    }

    }



    @Stateful
    公共类EmployeeServiceBean实现EmployeeService {

    //因为bean是有状态的,可以存储为实例变量并在多个方法中使用
    private EntityManagerFactory emf;
    私有EntityManager em;

    @PostConstruct //在EJB构造和会话启动时自动调用
    public void init(){
    emf = Persistence.createEntityManagerFactory(EmployeeService);
    em = emf.createEntityManager();
    }

    //交易方法
    public void createEmployee(){
    Employee emp = ...; //设置一些数据
    em.joinTransaction(); //在JTA tx之前创建 - 手动加入
    em.persist(emp);
    }

    //交易方法
    public void updateEmployee(){
    Employee emp = em.find(...); //加载员工
    //如果两个方法在同一个会话中调用,则不要进行连接 - 只能调用一次:
    // em.joinTransaction(); //在JTA tx之前创建 - 手动加入
    emp.set(...); //更改一些数据
    //没有持久调用 - 使用commit
    自动刷新}

    @Remove //当EJB会话结束时自动调用
    public void cleanup( ){
    em.close();
    emf.close();
    }
    // ... ...
    }


  • B)使用BEAN管理的JTA事务的EJB



    使用@TransactionManagement.BEAN。

    注入JTA UserTransaction接口,因此bean可以直接标记JTA

    通过UserTransaction.begin()/ commit()/ rollback()手动标记/同步事务。

    确保EntityManager加入JTA事务 - 创建EM活动的JTA事务上下文或调用em.joinTransaction()。



    示例:

      @TransactionManagement(TransactionManagement.BEAN)
    @Stateless
    公共类EmployeeServiceBean实现EmployeeService {

    //注入JTA事务接口
    @Resource UserTransaction jtaTx ;

    public void createEmployee(){
    EntityManagerFactory emf = Persistence.createEntityManagerFactory(EmployeeService);
    EntityManager em = emf.createEntityManager();
    try {
    jtaTx.begin();
    try {
    em.joinTransaction();
    员工emp = ...; //设置一些数据
    em.persist(emp);
    //其他数据& em操作...
    //调用其他EJB参与同一事务...
    } finally {
    jtaTx.commit();
    }
    } catch(例外e){
    //处理来自UserTransaction方法的异常
    // ... ...
    }

    员工emp = ...; //设置一些数据
    //不需要手动连接 - 在活动tx上下文中创建的em,自动连接:
    // em.joinTransaction();
    em.persist(emp);
    em.close(); //注意:在提交JTA tx之前可以关闭em。
    //持久化上下文仍然存在于JTA tx中。
    //数据仍将被刷新并提交,而持久性
    //在tx提交后的某个时间内删除了上下文。
    emf.close();
    }

    }


  • C)POJO /非EJB与手动编码(bean管理)资源本地事务(不是JTA)



    只需使用JPA EntityTransaction接口进行tx分界(通过em.getTransaction()获得例如:

     公共类ProjectServlet扩展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();
    }终于{
    tx.commit();
    }
    } catch(例外e){
    //处理来自EntityTransaction方法的异常
    // ... ...
    }
    // ...
    }
    }


  • D)POJO /非EJB与手动编码(POJO管理)JTA事务



    这假设POJO /组件在某个具有JTA支持的容器中运行。

    如果在一个Java EE容器,可以使用Java EE资源注入JTA UserTransaction接口。

    (或者,可以显式查找JTA接口的句柄并对其进行分界,然后调用em.getTransaction()。joinTransaction () - 参见JTA规范。)



    示例:

      public class ProjectServlet扩展了HttpServlet {

    @Resource UserTransaction tx;
    @EJB ProjectService bean;

    protected void doPost(HttpServletRequest request,HttpServletResponse response)
    抛出ServletException,IOException {
    // ... ...
    try {
    tx.begin( );
    try {
    bean.assignEmployeeToProject(projectId,empId);
    bean.updateProjectStatistics();
    EntityManagerFactory emf = Persistence.createEntityManagerFactory(myPU);
    EntityManager em = emf.createEntityManager();
    //应该能够避免显式调用加入事务。
    //应该自动加入,因为EM在活动的tx上下文中创建。
    // em.joinTransaction();
    //对这里的数据进行操作
    em.close();
    emf.close();
    }终于{
    tx.commit();
    }
    } catch(例外e){
    //处理来自UserTransaction方法的异常
    // ... ...
    }
    // ...
    }
    }



I'm developing a webapp which needs access to two different database servers (H2 and Oracle). The container is an Apache Tomee 1.5.1 and I'm using the Java EE stack with libraries provided in it (JSF, JPA, CDI, EJB, etc.).

I'm trying to use two entity managers inside an XA transaction to extract data from the Oracle database and persist it in the H2 after transforming it, BUT all the queries are executed against the H2 database no matter the entity manager I use. Any help?

EDIT: I found that if I try to access the entity managers in inverse order, they behavior is the same but accessing to Oracle. I.e.: the entity managers stay with the first database accessed.

The EJB where this happens (calling service.getFoo() from JSF):

@Named
@Stateless
public class Service {
    @Inject
    @OracleDatabase
    private EntityManager emOracle;

    @Inject
    @H2Database
    private EntityManager emH2;

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public List<Foo> getFoo() {
        TypedQuery<Foo> q = emH2.createQuery(
                "SELECT x FROM Foo f", Foo.class);
        List<Foo> l = q.getResultList();
        if (l == null || l.isEmpty()) {
            update();
        }

        return q.getResultList();
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void update() {
        // FAIL: This query executes against H2 with Oracle entity manager!
        List<Object[]> l = emOracle.createNativeQuery("SELECT * FROM bar ").getResultList(); 

        //more stuff...
    }
}

The resource producer (CDI) for the entity managers (where @H2Database and @OracleDatabase are qualifiers):

public class Resources {
    @Produces
    @PersistenceContext(unitName = "OraclePU")
    @OracleDatabase
    private EntityManager emOracle;

    @Produces
    @PersistenceContext(unitName = "H2PU")
    @H2Database
    private EntityManager emH2;
}

My peristence.xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="H2PU"
        transaction-type="JTA">
        <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
        <jta-data-source>H2DS</jta-data-source>
        <class>my.app.h2.Foo</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
    </persistence-unit>

    <persistence-unit name="OraclePU" transaction-type="JTA">
        <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
        <jta-data-source>OracleDS</jta-data-source>
        <class>my.app.oracle.Bar</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
    </persistence-unit>
</persistence>

And finally, the data sources inside tomee.xml (there aren't any other data sources configured inside this file):

<Resource id="OracleDS" type="javax.sql.DataSource">
    jdbcDriver = oracle.jdbc.xa.client.OracleXADataSource
    jdbcUrl = jdbc:oracle:thin:@server:port:instance
    jtaManaged = true
    password = abcde
    userName = user
</Resource>

<Resource id="H2DS" type="javax.sql.DataSource">
    jdbcDriver=org.h2.jdbcx.JdbcDataSource
    jdbcUrl=jdbc:h2:h2/db;AUTO_SERVER=TRUE
    jtaManaged = true
    password = edcba
    userName = user
</Resource>

解决方案

Container Managed Persistence Contexts

When using container-managed persistence contexts (as you are via @PersistenceContext annotations), the JPA spec specifies that only ONE persistence context may be associated with a JTA transaction.

The persistence context is created by the Java EE container. Despite appearances of the code (@PersistenceContext annotations seem to suggest that PC is injected directly into your EntityManager instance variables), the persistence context is actually stored as a reference WITHIN THE JTA TRANSACTION. Each time an EntityManager operation occurs it does not refer to it's own internal persistence context. Instead it does a special operation because it is container managed - it always looks up the persistence context within the JTA Transaction and uses that. This is called JTA persistence context propagation.

Some Quotes from the JPA spec:

When a container-managed entity manager is used, the lifecycle of the persistence context is always managed automatically, transparently to the application, and the persistence context is propagated with the JTA transaction.

Container-managed Transaction-scoped Persistence Context

... A new persistence context begins when the container-managed entity manager is invoked[76] in the scope of an active JTA transaction, and there is no current persistence context already associated with the JTA transaction. The persistence context is created and then associated with the JTA transaction.

Container-managed Extended Persistence Context

... A container-managed extended persistence context can only be initiated within the scope of a stateful session bean. It exists from the point at which the stateful session bean that declares a dependency on an entity manager of type PersistenceContextType.EXTENDED is created, and is said to be bound to the stateful session bean. The dependency on the extended persistence context is declared by means of the PersistenceContext annotation or persistence-context-ref deployment descriptor element. The persistence context is closed by the container when the @Remove method of the stateful session bean completes (or the stateful session bean instance is otherwise destroyed).

Requirements for Persistence Context Propagation

... If a component is called and there is no JTA transaction ..., the persistence context is not propagated. • Invocation of an entity manager defined with PersistenceContext- Type.TRANSACTION will result in use of a new persistence context. • Invocation of an entity manager defined with PersistenceContext- Type.EXTENDED will result in the use of the existing extended persistence context bound to that component.

... If a component is called and the JTA transaction is propagated into that component: • If the component is a stateful session bean to which an extended persistence context has been bound and there is a different persistence context bound to the JTA transaction, an EJBException is thrown by the container. • Otherwise, if there is a persistence context bound to the JTA transaction, that persistence context is propagated and used.

So that's your problem. The obvious $64 question: WHY does the spec ask for this???

Well, it's because it's a deliberate trade-off that brings powerful EntityManager magic to EJBs.

Using JTA transactions to propagate a single persistence context has a limitation: transactions cannot straddle multiple persistence contexts, so can't straddle multiple databases.

However, it also has a tremendous advantage: any entityManager declared in EJBs can automatically share the same persistence context and hence can operate on the same set of JPA entities and partake in the same transaction. You can have a chain of EJBs calling other EJBs of any complexity and they all behave sensibly and consistently against the JPA entity data. And they also do not need the complexity of consistently intializing/sharing entity manager references across method invocations - the EntityManagers can be declared privately in each method. The implementation logic can be very simple.

The Answer To Your Problem: Use Application-Managed Persistence Contexts (via application-managed EntityManagers)

Declare your entityManager via one of these approaches:

// "Java EE style" declaration of EM
@PersistenceUnit(unitName="H2PU")
EntityManagerFactory emfH2;
EntityManager emH2 = emfH2.createEntityManager();

OR

// "JSE style" declaration of EM
EntityManagerFactory emfH2 = javax.persistence.Persistence.createEntityManagerFactory("H2PU");
EntityManager emH2 = emfH2.createEntityManager();

and the same for emfOracle & emOracle.    

You must call em.close() when you are finished with each EM - prefereably via a final { } clause or via a Java 7 try-with-resources statement.

Application-managed EMs still partake in (i.e. synchronise with) JTA transactions. Any number of application-managed EMs can partake in a single JTA transaction - but none of these will ever have their persistence context associated with or propagated to any container managed EM.

If the EntityManager is created outside the context of a JTA transaction (before the transaction commenced), then you must ask it explicity to join the JTA transaction:

// must be run from within Java EE code scope that already has a JTA 
// transaction active:
em.joinTransaction();  

Or even simpler, if the EntityManager is created inside the context of a JTA transaction, then the application-managed EntityManager automatically joins the JTA transaction implicity - no joinTransaction() needed.

So Application-managed EMs can have a JTA transaction that straddles multiple databases. Of course, you can alway run a local resource JDBC transaction independent of JTA:

EntityTransaction tx = em.getTransaction();  
tx.begin();

// ....

tx.commit();

EDIT: EXTRA DETAILS for Transaction Management with Application-Managed Entity Managers

WARNING: the code samples below are for educational use - I've typed them off the top of my head to help explain my points & haven't had time to compile/debug/test.

The default @TransactionManagement parameter for EJBs is TransactionManagement.CONTAINER and the default @TransactionAttribute parameter for EJB methods is TransactionAttribute.REQUIRED.

There are four permutations for transaction management:

  • A) EJB with CONTAINER managed JTA transactions

    This is the preferred Java EE approach.
    EJB class @TransactionManagement annotation:
    must set to TransactionManagement.CONTAINER explicitly or omit it to implicitly use the default value.
    EJB method @TransactionAttribute annotation: must set to TransactionAttribute.REQUIRED explicitly or omit it to implicity use the default value. (Note: if you had a different business scenario, you could use TransactionAttribute.MANDATORY or TransactionAttribute.REQUIRES_NEW if their semantics matched your needs.)
    Application-managed entity managers:
    they must be created via Persistence.createEntityManagerFactory("unitName") and emf.createEntityManager(), as described above.
    Join the EntityManagers with the JTA transaction:
    Create the EntityManagers WITHIN a transactional EJB method and they will automatically join the JTA transaction. OR if EntityManagers are created beforehand, call em.joinTransaction() within a transaction EJB method.
    Call EntityManager.close() when you are finished using them. That should be all that's required.

    Basic Examples - just use more EntityManagers for transaction across multiple DBs:

    @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) EJB with BEAN managed JTA transactions

    Use @TransactionManagement.BEAN.
    Inject the JTA UserTransaction interface, so the bean can directly mark JTA transactions.
    Manually mark/synchronize the transaction via UserTransaction.begin()/commit()/rollback().
    Ensure the EntityManager joins the JTA transaction - either create the EM in an active JTA transaction context OR call em.joinTransaction().

    Examples:

    @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 with hand-coded (bean managed) resource local transactions (not JTA)

    Simply use the JPA EntityTransaction interface for tx demarcation (obtained via em.getTransaction()).

    Example:

    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/Non-EJB with hand-coded (POJO-managed) JTA transactions

    This assumes the POJO/component is running in some container that has JTA support.
    If in a Java EE container, can use Java EE resource injection of JTA UserTransaction interface.
    (Alternatively, can explicitly lookup a handle to the JTA interface and do demarcation on it, then call em.getTransaction().joinTransaction() - see JTA spec.)

    Example:

    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
               // ...
            }
        // ...
        }
    }
    

这篇关于为什么具有分离数据源的不同持久性单元会查询相同的数据源?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆