Spring:1 个事务中有 2 个 EntityManagerFactory

Spring : 2 EntityManagerFactory in 1 transaction

我想在同一个应用程序中访问两个数据库。对于每个数据库连接,我都有一个 EntityManagerFactory。
我的问题是:我无法在同一事务中合并两个数据库中的两个实体。

这是我的 beans.xml 文件:

    <context:component-scan base-package="com.example.testdatabase.business" />
    <context:component-scan base-package="com.example.testdatabase.service" />
    <context:component-scan base-package="com.example.testdatabase.ui" />

    <bean id="entityManagerFactoryOrder"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSourceOrder" />
        <property name="packagesToScan" value="com.example.testdatabase.business.order" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">validate</prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="validationQuery">select 1 from dual</prop>
                <prop key="testOnBorrow">true</prop>
            </props>
        </property>
        <property name="persistenceUnitName" value="orderPU" />
    </bean>

    <bean id="dataSourceOrder" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="java:jboss/datasources/OrderCita" />
    </bean>

    <bean id="transactionManagerOrder" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactoryOrder" />
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
        </property>
    </bean>

    <bean id="entityManagerFactoryPerson"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSourcePerson" />
        <property name="packagesToScan" value="com.example.testdatabase.business.person" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">create</prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
        <property name="persistenceUnitName" value="personPU" />
    </bean>

    <bean id="dataSourcePerson" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="java:jboss/datasources/Person" />
    </bean>

    <bean id="transactionManagerPerson" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactoryPerson" />
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
        </property>
    </bean>

    <tx:annotation-driven/>

我的存储库:

@Repository
public class Repo {

   @PersistenceContext(unitName="orderPU")
   @Qualifier(value="entityManagerFactoryOrder")
   protected EntityManager emOrder;

   @PersistenceContext(unitName="personPU")
   @Qualifier(value="entityManagerFactoryPerson")
   protected EntityManager emPerson;

   public void save() {
       Order o = new Order();
       o.setTheString("the order");

       emOrder.merge(o);
       emOrder.close();

       Person p = new Person();
       p.setTheString("the person");

       emPerson.merge(p);
       emPerson.close();
    }
}

我的服务:

@Service
public class Manager {
   @Autowired
   private Repo repo;

   @Transactional(value="transactionManagerOrder")
   public void save1() {
       repo.save();
   }

   @Transactional(value="transactionManagerPerson")
   public void save2() {
       repo.save();
   }

   @Transactional
   public void save3() {
       repo.save();
   }
}

方法"save1()"只合并一个订单。
方法"save2()"只合并一个人。
我想用同一笔交易合并一个订单和一个人。

感谢您的帮助

PS : 对不起我的英文...


* 编辑 *
我尝试使用 bitronix,但现在出现空指针异常。
我还在 JpaProperties 中添加了这一行:
org.hibernate.engine.transaction.jta.p‌ latform.internal.BitronixJtaPlatform

这是我的新 beans.xml 文件:

    <bean class="bitronix.tm.TransactionManagerServices"
    factory-method="getConfiguration" id="bitronixConfiguration" />

<bean class="bitronix.tm.TransactionManagerServices" depends-on="bitronixConfiguration"
    destroy-method="shutdown" factory-method="getTransactionManager" id="bitronixTransactionManager" />

<bean class="bitronix.tm.resource.jdbc.PoolingDataSource"
    destroy-method="close" id="datasourceOrder" init-method="init">
    <property name="allowLocalTransactions" value="true" />
    <property name="className" value="org.postgresql.xa.PGXADataSource" />
    <property name="driverProperties">
        <props>
            <prop key="password">pwd</prop>
            <prop key="url">jdbc:postgresql://localhost:5432/order</prop>
            <prop key="user">user</prop>
        </props>
    </property>
    <property name="maxPoolSize" value="5" />
    <property name="minPoolSize" value="1" />
    <property name="uniqueName" value="exampleDS" />
</bean>

<bean class="bitronix.tm.resource.jdbc.PoolingDataSource"
    destroy-method="close" id="datasourcePerson" init-method="init">
    <property name="allowLocalTransactions" value="true" />
    <property name="className" value="org.postgresql.xa.PGXADataSource" />
    <property name="driverProperties">
        <props>
            <prop key="password">pwd</prop>
            <prop key="url">jdbc:postgresql://localhost:5432/person</prop>
            <prop key="user">user</prop>
        </props>
    </property>
    <property name="maxPoolSize" value="5" />
    <property name="minPoolSize" value="1" />
    <property name="uniqueName" value="sampleDS" />
</bean>

<bean id="entityManagerFactoryOrder"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="jtaDataSource" ref="datasourceOrder" />
    <property name="packagesToScan" value="com.example.testdatabase.business.order" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
    </property>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.transaction.jta.platform">org.hibernate.engine.transaction.jta.p‌​latform.internal.BitronixJtaPlatform</prop>
            <prop key="hibernate.hbm2ddl.auto">validate</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="validationQuery">select 1 from dual</prop>
            <prop key="testOnBorrow">true</prop>
        </props>
    </property>
    <property name="persistenceUnitName" value="orderPU" />
</bean>

<bean id="entityManagerFactoryPerson"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="jtaDataSource" ref="datasourcePerson" />
    <property name="packagesToScan" value="com.example.testdatabase.business.person" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
    </property>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.transaction.jta.platform">org.hibernate.engine.transaction.jta.p‌​latform.internal.BitronixJtaPlatform</prop>
            <prop key="hibernate.hbm2ddl.auto">create</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
            <prop key="hibernate.show_sql">true</prop>
        </props>
    </property>
    <property name="persistenceUnitName" value="personPU" />
</bean>

<bean class="org.springframework.transaction.jta.JtaTransactionManager"
    id="transactionManager">
    <property name="transactionManager" ref="bitronixTransactionManager" />
    <property name="userTransaction" ref="bitronixTransactionManager" />
</bean>

<tx:annotation-driven />

在管理器 class 中,我删除了 @Transactional 注释。
在 Repo class 中,我删除了 @Qualifier 注释。

堆栈跟踪:

00:47:22,665 SEVERE [com.vaadin.server.DefaultErrorHandler] (http-localhost/127.0.0.1:8080-2) : java.lang.NullPointerException
at org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory.isJoinableJtaTransaction(CMTTransactionFactory.java:63) [hibernate-core-4.1.9.Final.jar:4.1.9.Final]
at org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory.isJoinableJtaTransaction(CMTTransactionFactory.java:39) [hibernate-core-4.1.9.Final.jar:4.1.9.Final]
at org.hibernate.engine.transaction.internal.TransactionCoordinatorImpl.isTransactionJoinable(TransactionCoordinatorImpl.java:295) [hibernate-core-4.1.9.Final.jar:4.1.9.Final]
at org.hibernate.ejb.AbstractEntityManagerImpl.joinTransaction(AbstractEntityManagerImpl.java:1205) [hibernate-entitymanager-4.1.9.Final.jar:4.1.9.Final]
at org.hibernate.ejb.AbstractEntityManagerImpl.postInit(AbstractEntityManagerImpl.java:178) [hibernate-entitymanager-4.1.9.Final.jar:4.1.9.Final]
at org.hibernate.ejb.EntityManagerImpl.<init>(EntityManagerImpl.java:89) [hibernate-entitymanager-4.1.9.Final.jar:4.1.9.Final]
at org.hibernate.ejb.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:179) [hibernate-entitymanager-4.1.9.Final.jar:4.1.9.Final]
at org.hibernate.ejb.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:174) [hibernate-entitymanager-4.1.9.Final.jar:4.1.9.Final]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.6.0_45]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) [rt.jar:1.6.0_45]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) [rt.jar:1.6.0_45]
at java.lang.reflect.Method.invoke(Method.java:597) [rt.jar:1.6.0_45]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.invokeProxyMethod(AbstractEntityManagerFactoryBean.java:376) [spring-orm-3.2.5.RELEASE.jar:3.2.5.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean$ManagedEntityManagerFactoryInvocationHandler.invoke(AbstractEntityManagerFactoryBean.java:519) [spring-orm-3.2.5.RELEASE.jar:3.2.5.RELEASE]
at com.sun.proxy.$Proxy38.createEntityManager(Unknown Source)
at org.springframework.orm.jpa.EntityManagerFactoryUtils.doGetTransactionalEntityManager(EntityManagerFactoryUtils.java:202) [spring-orm-3.2.5.RELEASE.jar:3.2.5.RELEASE]
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:211) [spring-orm-3.2.5.RELEASE.jar:3.2.5.RELEASE]
at com.sun.proxy.$Proxy40.merge(Unknown Source)
at com.example.testdatabase.business.repo.Repo.save(Repo.java:29) [classes:]
at com.example.testdatabase.service.Manager.save3(Manager.java:26) [classes:]
at com.example.testdatabase.service.Manager$$FastClassBySpringCGLIB$df77bc4.invoke(<generated>) [spring-core-4.1.6.RELEASE.jar:]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) [spring-core-4.1.6.RELEASE.jar:4.1.6.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:717) [spring-aop-4.1.6.RELEASE.jar:4.1.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) [spring-aop-4.1.6.RELEASE.jar:4.1.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.proceedWithInvocation(TransactionInterceptor.java:98) [spring-tx-4.0.5.RELEASE.jar:4.0.5.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262) [spring-tx-4.0.5.RELEASE.jar:4.0.5.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) [spring-tx-4.0.5.RELEASE.jar:4.0.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.1.6.RELEASE.jar:4.1.6.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653) [spring-aop-4.1.6.RELEASE.jar:4.1.6.RELEASE]
at com.example.testdatabase.service.Manager$$EnhancerBySpringCGLIB$$ab725425.save3(<generated>) [spring-core-4.1.6.RELEASE.jar:]
at com.example.testdatabase.TestdatabaseUI.buttonClick(TestdatabaseUI.java:56) [classes:]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.6.0_45]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) [rt.jar:1.6.0_45]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) [rt.jar:1.6.0_45]
at java.lang.reflect.Method.invoke(Method.java:597) [rt.jar:1.6.0_45]
at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:508) [vaadin-server-7.5.0.jar:7.5.0]
at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:198) [vaadin-server-7.5.0.jar:7.5.0]
at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:161) [vaadin-server-7.5.0.jar:7.5.0]
at com.vaadin.server.AbstractClientConnector.fireEvent(AbstractClientConnector.java:995) [vaadin-server-7.5.0.jar:7.5.0]
at com.vaadin.ui.Button.fireClick(Button.java:393) [vaadin-server-7.5.0.jar:7.5.0]
at com.vaadin.ui.Button.click(Button.java:61) [vaadin-server-7.5.0.jar:7.5.0]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.6.0_45]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) [rt.jar:1.6.0_45]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) [rt.jar:1.6.0_45]
at java.lang.reflect.Method.invoke(Method.java:597) [rt.jar:1.6.0_45]
at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:158) [vaadin-server-7.5.0.jar:7.5.0]
at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:118) [vaadin-server-7.5.0.jar:7.5.0]
at com.vaadin.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:313) [vaadin-server-7.5.0.jar:7.5.0]
at com.vaadin.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:202) [vaadin-server-7.5.0.jar:7.5.0]
at com.vaadin.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:95) [vaadin-server-7.5.0.jar:7.5.0]
at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:41) [vaadin-server-7.5.0.jar:7.5.0]
at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1408) [vaadin-server-7.5.0.jar:7.5.0]
at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:350) [vaadin-server-7.5.0.jar:7.5.0]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:847) [jboss-servlet-api_3.0_spec-1.0.2.Final-redhat-1.jar:1.0.2.Final-redhat-1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:295) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:149) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
at org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:50) [jboss-as-jpa-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
at org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:50) [jboss-as-jpa-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:169) [jboss-as-web-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:145) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:97) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:102) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:336) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:856) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:653) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:920) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
at java.lang.Thread.run(Thread.java:662) [rt.jar:1.6.0_45]

您当前使用的是本地事务管理器,它一次只针对一个数据存储管理事务。要管理跨越多个数据存储的事务,您将需要一个符合 JTA 的事务管理器。

有关使用 2 EntityManagers 的 Bitronix JTA 事务管理器的示例,请参阅 this sample app