Spring, OpenJPA, множественная единица персистентности

Существует весеннее веб-приложение Apache James (почтовый сервер Java).

Он использует openjpa. Он имеет единицу персистентности, источник данных и определение фабрики entitymanager.

Я должен манипулировать им, чтобы использовать еще один блок сохранения для внешней БД.

Я добавил еще один модуль в файл persistence.xml.

<persistence-unit name="James" transaction-type="RESOURCE_LOCAL">
        <!-- Mailbox stuff-->
        <class>org.apache.james.mailbox.jpa.mail.model.JPAMailbox</class>
        <class>org.apache.james.mailbox.jpa.mail.model.JPAUserFlag</class>
        <class>org.apache.james.mailbox.jpa.mail.model.openjpa.AbstractJPAMessage</class>
        <class>org.apache.james.mailbox.jpa.mail.model.openjpa.JPAMessage</class>
        <class>org.apache.james.mailbox.jpa.mail.model.openjpa.JPAMessage</class>
        <class>org.apache.james.mailbox.jpa.mail.model.JPAProperty</class>
        <class>org.apache.james.mailbox.jpa.user.model.JPASubscription</class>
        <class>org.apache.james.domainlist.jpa.model.JPADomain</class>
        <class>org.apache.james.user.jpa.model.JPAUser</class>
        <class>org.apache.james.rrt.jpa.model.JPARecipientRewrite</class>

        <properties>
            <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/>
            <property name="openjpa.jdbc.MappingDefaults" value="ForeignKeyDeleteAction=cascade, JoinForeignKeyDeleteAction=cascade"/>
            <property name="openjpa.jdbc.SchemaFactory" value="native(ForeignKeys=true)"/>
            <property name="openjpa.jdbc.QuerySQLCache" value="false"/>
        </properties>

    </persistence-unit>


    <persistence-unit name="myPU" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <class>package.EmailAddress</class>
    <class>package.Message</class>
       <properties>
            <property name="javax.persistence.jdbc.user" value="root" />
            <property name="javax.persistence.jdbc.password" value="root" />
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/kepsDb" />
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
            <property name="hibernate.hbm2ddl.auto" value="none" />
            <property name="hibernate.show_sql" value="false" />
            <property name="hibernate.dialect" value=" org.hibernate.dialect.MySQLDialect" />

            <property name="hibernate.max_fetch_depth" value="0"  />
            <property name="hibernate.cache.use_second_level_cache" value="true" />
            <property name="hibernate.cache.use_query_cache" value="false" />
            <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory" />

            <property name="hibernate.ejb.naming_strategy" value="web.app.persistence.util.AppImprovedNamingStrategy"/>

        </properties>

    </persistence-unit>

Я не определяю вторую фабрику диспетчера сущностей в spring-server.xml, вместо этого я создаю свою собственную фабрику управления сущностями, встроенную с помощью:

EntityManagerFactory emf=Persistence.createEntityManagerFactory("myPU");
        EntityManager entityManager=emf.createEntityManager();
entityManager.getTransaction().begin();

Но я получаю исключение:

Caused by: org.springframework.beans.FatalBeanException: Unable to execute lifecycle method on beanmailetcontext; nested exception is <openjpa-2.1.0-r422266:1071316 nonfatal user error> org.apache.openjpa.persistence.InvalidStateException: This operation cannot be performed while a Transaction is active.

person merveotesi    schedule 28.09.2012    source источник
comment
Вы создаете EMF с помощью модуля myPU и говорите в своем вопросе, что он использует OpenJPA, но, похоже, использует Hibernate...   -  person DataNucleus    schedule 28.09.2012


Ответы (2)


myPU настроен на использование транзакции JTA. Вызов entityManager.getTransaction() при использовании JTA вызовет исключение, так как этот метод предполагается использовать с типом транзакции RESOURCE_LOCAL.

Я не знаю, связаны ли с этим ваши опубликованные сообщения об исключениях, но вы можете попробовать изменить <persistence-unit> из myPU на:

<persistence-unit name="myPU" transaction-type="RESOURCE_LOCAL">

Обратите внимание, что если вам нужно получить доступ к обеим базам данных в одной и той же транзакции, вы должны использовать JTA.

person Ken Chan    schedule 28.09.2012
comment
Я понятия не имею, какое у вас исключение. Но даже если вы решите это исключение, вам все равно придется изменить тип транзакции на RESOURCE_LOCAL, если вы используете entityManager.getTransaction().begin() для запуска перехода. Кстати, DataNucleus прав, вы используете OpenJPA и спящий режим одновременно? Почему вы говорите, что используете OpenJPA, но myPU содержит конфигурацию спящего режима?? - person Ken Chan; 28.09.2012

Следующий код объясняет, как настроить несколько единиц сохраняемости с помощью JPA + spring:

Прежде всего, мы определяем два модуля сохраняемости в файле persistence.xml, назовем их unit1 и unit2 соответственно:

<persistence 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"
  version="1.0">

  <persistence-unit name="Unit1" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <properties>
      <property name="hibernate.archive.autodetection" value="class" />
      <property name="hibernate.connection.driver_class" value="oracle.jdbc.OracleDriver" />
      <property name="hibernate.connection.url" value="jdbc:oracle:thin:@my.company.com:1522:D1" />
      <property name="hibernate.connection.password" value="my_user" />
      <property name="hibernate.connection.username" value="my_password" />
    </properties>
  </persistence-unit>

  <persistence-unit name="Unit2" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <properties>
      <property name="hibernate.archive.autodetection" value="class" />
      <property name="hibernate.connection.driver_class" value="oracle.jdbc.OracleDriver" />
      <property name="hibernate.connection.url" value="jdbc:oracle:thin:@my.company.com:1522:D2" />
      <property name="hibernate.connection.password" value="my_user" />
      <property name="hibernate.connection.username" value="my_password" />
    </properties>
  </persistence-unit>
</persistence>

Поскольку мы имели дело с автономным Java-приложением, мы определили наши источники данных в контексте приложения Spring, но для веб-приложений обычно определяются ссылки JNDI на эти источники данных в самом файле persistence.xml.

В файле application-context.xml эти единицы персистентности называются так:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context-2.5.xsd
  http://www.springframework.org/schema/tx
  http://www.springframework.org/schema/tx/spring-tx.xsd">

  <context:annotation-config />
  <tx:annotation-driven />

  <bean id="dataSource1" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="oracle.jdbc.driver.OracleDriver" />
    <property name="jdbcUrl" value="jdbc:oracle:thin:@my.company.com:1521:D1" />
    <property name="user" value="my_user" />
   <property name="password" value="my_password" />
  </bean>

  <bean id="dataSource2" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
    <property name="url" value="jdbc:oracle:thin:@my.company.com:1521:D2" />
    <property name="username" value="my_user" />
    <property name="password" value="my_password" />
  </bean>

  <!-- DEFINITION OF BOTH ENTITY MANAGER FACTORIES -->

  <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource1" />
    <property name="persistenceUnitName" value="Unit1" />
    <property name="persistenceUnitManager" ref="persistenceUnitManager" />
    <property name="jpaVendorAdapter">
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="databasePlatform"
                  value="org.hibernate.dialect.Oracle10gDialect" />
      </bean>
    </property>
  </bean>

  <bean id="entityManagerFactory2"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource2" />
    <property name="persistenceUnitName" value="Unit2" />
    <property name="persistenceUnitManager" ref="persistenceUnitManager" />
    <property name="jpaVendorAdapter">
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="databasePlatform"
                  value="org.hibernate.dialect.Oracle10gDialect" />
      </bean>
    </property>
  </bean>

  <!-- PERSISTENCE UNIT MANAGER and TRANSACTION MANAGERS -->

  <bean id="persistenceUnitManager"
        class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
    <property name="dataSources">
      <map>
        <entry key="d1" value-ref="dataSource1" />
        <entry key="d2" value-ref="dataSource2" />
      </map>
    </property>
  </bean>

  <bean id="transactionManager"
        class="org.springframework.orm.jpa.JpaTransactionManager"
        p:entity-manager-factory-ref="entityManagerFactory" />

  <bean id="abwTransactionManager"
        class="org.springframework.orm.jpa.JpaTransactionManager"
        p:entity-manager-factory-ref="entityManagerFactory2" />
</beans>

Теперь все, что осталось сделать, это обозначить @PersistenceContext в ваших DAO следующим образом:

@Required
  @PersistenceContext(unitName = "Unit1")
  public void setEntityManager(final EntityManager entityManager) {
      this.entityManager = entityManager;
  }
person Anshu    schedule 28.09.2012
comment
если я определяю несколько entityManagerFactory, я не получаю уникальную ошибку entityManagerFactory. - person merveotesi; 29.09.2012
comment
Вы указали, какую единицу персистентности вводить? @PersistenceContext(unitName = Unit1) - person Anshu; 29.09.2012
comment
Указание unitName в PersistenceContext ничего не меняет, Spring по-прежнему не знает, какую фабрику использовать для создания entitymanager - person Krzysztof Krasoń; 08.11.2012