spring无法管理两个实体经理

时间:2014-11-26 10:16:30

标签: spring jpa transactions entitymanager

大家好,我有一个有两个不同数据库的应用程序。 第一个数据库用于我们的业务应用程序,第二个数据库属于我们正在使用的框架。

我有一个女经理,我需要访问两个dbs来执行某些操作。 我为我的应用程序配置了两个entityManagerFactory,每个数据源一个,两个事务管理器,每个entityManagerFactory一个......

这是我的配置

我的persistence.xml

<persistence version="1.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_1_0.xsd">
  <persistence-unit name="ENPIM_BD_PU" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <non-jta-data-source>jdbc/businessDataSource</non-jta-data-source>
    <class>it.aicof.projects.enpim.persistence.entities.AccountingRecords</class>
    .....
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
      <property name="hibernate.show_sql" value="false"/>
    </properties>
  </persistence-unit>
  <persistence-unit name="ENPIM_SERV_PU" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <class>it.aicof.projects.enpim.persistence.entities.Authusers</class>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
      <property name="hibernate.show_sql" value="false"/>
    </properties>
  </persistence-unit>
</persistence>

我的app-context.xml

<context:annotation-config/>
    <context:component-scan base-package="it.aicof.projects.*"/>
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
    <bean id="dataSourceBusiness" class="com.mchange.v2.c3p0.ComboPooledDataSource"
          destroy-method="close">
            <property name="driverClass" value="${profile.database.driverClassName}" />
            <property name="jdbcUrl" value="jdbc:postgresql://${profile.database.hostname}:${profile.database.port}/${profile.database.business}" />
            <property name="user" value="${profile.database.username}" />
            <property name="password" value="${profile.database.password}" />

        <!-- these are C3P0 properties -->
            <property name="acquireIncrement" value="${c3p0.acquireIncrement}" />
            <property name="initialPoolSize" value="${c3p0.initialPoolSize}" />
            <property name="minPoolSize" value="${c3p0.minPoolSize}" />
            <property name="maxPoolSize" value="${c3p0.maxPoolSize}" />
            <property name="maxIdleTime" value="${c3p0.maxIdleTime}" />
    </bean>

    <bean id="dataSourceServ" class="com.mchange.v2.c3p0.ComboPooledDataSource"
          destroy-method="close">
            <property name="driverClass" value="${profile.database.driverClassName}" />
            <property name="jdbcUrl" value="jdbc:postgresql://${profile.database.hostname}:${profile.database.port}/${profile.database.serv}" />
            <property name="user" value="${profile.database.username}" />
            <property name="password" value="${profile.database.password}" />

        <!-- these are C3P0 properties -->
            <property name="acquireIncrement" value="${c3p0.acquireIncrement}" />
            <property name="initialPoolSize" value="${c3p0.initialPoolSize}" />
            <property name="minPoolSize" value="${c3p0.minPoolSize}" />
            <property name="maxPoolSize" value="${c3p0.maxPoolSize}" />
            <property name="maxIdleTime" value="${c3p0.maxIdleTime}" />
    </bean>       

    <bean id="transactionManagerBusiness" class="org.springframework.orm.jpa.JpaTransactionManager"
           p:entityManagerFactory-ref="entityManagerFactory">
        <qualifier value="business" />   
    </bean>

    <bean id="transactionManagerServ" class="org.springframework.orm.jpa.JpaTransactionManager"
           p:entityManagerFactory-ref="entityManagerFactoryServ">
        <qualifier value="serv" />   
    </bean>

    <bean id="entityManagerFactory"
           class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
           p:dataSource-ref="dataSourceBusiness"
           p:jpaVendorAdapter-ref="jpaAdapter">
        <property name="loadTimeWeaver">
            <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
        </property>
        <property name="persistenceUnitName" value="ENPIM_BD_PU"></property>
    </bean>

    <bean id="entityManagerFactoryServ"
           class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
           p:dataSource-ref="dataSourceServ"
           p:jpaVendorAdapter-ref="jpaAdapter">
        <property name="loadTimeWeaver">
            <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
        </property>
        <property name="persistenceUnitName" value="ENPIM_SERV_PU"></property>
    </bean>    

    <bean id="jpaAdapter"
             class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
             p:database="POSTGRESQL" p:showSql="false"/>

这是我正在使用的代码

我的道歉

    @PersistenceContext(unitName = "ENPIM_BD_PU")
    protected EntityManager em; 

    @PersistenceContext(unitName = "ENPIM_SERV_PU")
    protected EntityManager emServ; 

    ....

 public void edit(AddressBook addressBook) throws NonexistentEntityException, Exception {
        try {

            addressBook = em.merge(addressBook);
        } catch (Exception ex) {
            Logger.getLogger(this.getClass().getName()).error("edit", ex);
            String msg = ex.getLocalizedMessage();
            if (msg == null || msg.length() == 0) {
                Integer id = addressBook.getId();
                if (findAddressBook(id) == null) {
                    throw new NonexistentEntityException("The addressBook with id " + id + " no longer exists.");
                }
            }
            throw ex;
        } 
    }   

    public void createBeneficiaryLoginAccount(String username, String password) throws Exception{
         try {
            Query q = emServ.createNativeQuery("INSERT INTO authusers(username, passwd, registrationdate, lastaccess, lastpasswordchange, active)" +
                        " VALUES (?, ?, ?, ?, ?,  ?)");
            q.setParameter(1, username);
            q.setParameter(2, password);
            q.setParameter(3, new Date());
            q.setParameter(4, new Date());
            q.setParameter(5, new Date());
            q.setParameter(6, 1);
            q.executeUpdate();
        }catch (Exception ex) {
            Logger.getLogger(this.getClass().getName()).error("createBeneficiaryAccount", ex);
            throw ex;
        }
    }

我的经理

@Override
public void createBeneficiaryAccount(AddressBook addressBook, String password) {
    String username = addressBook.getUsername();
    try {
        AddressBook ab = this.addressBookJpaDAO.findAddressBook(addressBook.getId());
        ab.setUsername(username);
        //creo l'utente sul db serv e gli assegno il ruolo di beneficiario
        this.createBeneficiaryLoginAccount(username, password);

        this.createBeneficiaryUsername(ab, password);
    } catch (Throwable t) {
        Logger.getLogger(this.getClass().getName()).error("", t);
    }
}

@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class, value = "serv")
private void createBeneficiaryLoginAccount(String username, String password) throws Throwable {
    try {
        //creo l'utente sul db serv e gli assegno il ruolo di beneficiario
        this.addressBookJpaDAO.createBeneficiaryLoginAccount(username, password);
    } catch (Throwable t) {
        Logger.getLogger(this.getClass().getName()).error("createLoginAccount", t);
        throw t;
    }
}   

@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class, value = "business")
private void createBeneficiaryUsername(AddressBook addressBook, String password) throws Throwable {
    try {
        this.addressBookJpaDAO.edit(addressBook);
    } catch (Throwable t) {
        Logger.getLogger(this.getClass().getName()).error("createBeneficiaryUsername", t);
        throw t;
    }
}

当调用使用第二个entityManager的方法时,我得到异常

2014-11-26 11:10:35,844 ERROR controllers.AddressBookJpaController - createBeneficiaryAccount
javax.persistence.TransactionRequiredException: Executing an update/delete query
    at org.hibernate.jpa.spi.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:71)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
.....

并调试应用程序我发现当我们尝试使用第二个entityManager时我们有这个错误

Exception occurred in target VM: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead 
java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:198)
    at com.sun.proxy.$Proxy145.getTransaction(Unknown Source)
    at it.aicof.projects.enpim.persistence.controllers.AddressBookJpaController.createBeneficiaryLoginAccount(AddressBookJpaController.java:854)

我在网上看一些例子,但所有配置与我的相同。 那么..为什么它不起作用?

出了什么问题?

提前致谢

安德烈

1 个答案:

答案 0 :(得分:0)

您在私人方法上注释@Transactional。这不适用于Spring Aop Proxy Beans。

最快捷的方法是将两种方法都设置为公开

@Transactional("serv")
public void createBeneficiaryLoginAccount(String username, String password) throws Throwable {
    try {
        //creo l'utente sul db serv e gli assegno il ruolo di beneficiario
        this.addressBookJpaDAO.createBeneficiaryLoginAccount(username, password);
    } catch (Throwable t) {
        Logger.getLogger(this.getClass().getName()).error("createLoginAccount", t);
        throw t;
    }
}   

@Transactional("business")
public void createBeneficiaryUsername(AddressBook addressBook, String password) throws Throwable {
    try {
        this.addressBookJpaDAO.edit(addressBook);
    } catch (Throwable t) {
        Logger.getLogger(this.getClass().getName()).error("createBeneficiaryUsername", t);
        throw t;
    }
}

或者你可能想对AOP使用AspectJ模式,但这是另一层令人头疼的问题恕我直言。

请参阅:Does Spring @Transactional attribute work on a private method?