Как настроить несколько источников данных с помощью Spring и JPA

В нашем приложении мы хотим установить несколько источников данных с помощью Spring и JPA. Следовательно, мы создали 2 объекта entityManagerFactory, 2 источника данных и 2 менеджера transaction-.

web.xml

 <param-value>
    /WEB-INF/a_spring.xml
    /WEB-INF/b_spring.xml
 </param-value>

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<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="db1" transaction-type="RESOURCE_LOCAL">
        <class>com.rh.domain.RcA</class>
    </persistence-unit>

      <persistence-unit name="db2" transaction-type="RESOURCE_LOCAL">
      <class>com.rh.domain.Rcb</class>
    </persistence-unit>
</persistence>

a_spring.xml

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

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

      <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>  
      <bean id = "RcMaintenanceService" class="com.rh.services.RcAbcMaintenanceServiceImpl" autowire="byName" />

    <aop:config>
            <aop:pointcut id="rOperation" expression="execution(* com.rh.services.*.*(..))"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="rOperation"/>
        </aop:config>

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                   <tx:method name="*"/>
            </tx:attributes>
        </tx:advice>

    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
            <property name="jndiName" value="java:comp/env/jdbc/db1" />
        </bean> 
        <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
            <property name="entityManagerFactory" ref="entityManagerFactory"/>
            <property name="dataSource" ref="dataSource"/>
        </bean>

        <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
               <property name="persistenceUnitName" value="db1" />     
            <property name="dataSource" ref="dataSource"/>
            <property name="jpaVendorAdapter">
                <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
                    <property name="showSql" value="true"/>
                    <property name="generateDdl" value="false"/>
                    <property name="database" value="MYSQL" />
                    <property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect"/>
                </bean>
            </property>
            <property name="jpaDialect">
                <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect">
                </bean>
            </property>
        </bean>

Я также объявляю другое entityManagetFactory, Transaction Manager и dataSource для b_spring.xml.

ошибка

Инициализация компонента не выполнена; Вложенное исключение - org.springframework.beans.factory.NoSuchBeanDefinitionException: не определен уникальный bean-тип типа [javax.persistence.EntityManagerFactory]: ожидаемый единственный компонент, но найден 2 вызван: org.springframework.beans.factory.NoSuchBeanDefinitionException: нет уникального компонента типа [javax.persistence.EntityManagerFactory]: ожидаемый одиночный бит, но найден 2 на org.springframework.beans.factory.BeanFactoryUtils.beanOfTypeIncludingAncestors(BeanFactoryUtils.java:303) на org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor. findDefaultEntityManagerFactory (PersistenceAnnotationBeanPostProcessor.java:451) в org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:428) в org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor $ AnnotatedMember.resolveEntityManager(PersistenceAnnotationBeanPostProcessor.java: 582) в org.springframework.orm.jpa.suppo rt.PersistenceAnnotationBeanPostProcessor $ AnnotatedMember.resolve(PersistenceAnnotationBeanPostProcessor.java:553) в org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor $ AnnotatedMember.inject(PersistenceAnnotationBeanPostProcessor.java:489)

Ответ 1

В случае конфигурации нескольких источников данных нам необходимо определить, какой из них будет рассматриваться как первичный источник данных. мы можем указать, что с помощью @Primary аннотации в java config или primary=true в конфигурации XML bean.

Поскольку в XML создается два менеджера сущностей, нам нужно использовать @Qualifier чтобы указать, какой bean-компонент следует вводить где. В вашем случае, что-то вроде этого.

@PersistenceContext(unitName = "db1")
public void setEntityManager(@Qualifier("entityManagerFactory") EntityManager entityMgr) {
    this.em = entityMgr;
}

для конфигурации XML мы можем сделать что-то вроде этого

<bean id="BaseService" class="x.y.z.BaseService">
    <property name="em" ref="entityManagerFactory"/>
    <property name="em1" ref="entityManagerFactory1"/>
</bean>

<bean id = "RcMaintenanceService" class="com.rh.services.RcAbcMaintenanceServiceImpl" autowire="byName" parent="BaseService"/>

Ответ 2

Попробовали ли вы предоставить информацию о пакете, которая содержит ваш EntityManagerFactory bean?

Вы можете предоставить информацию о пакете как свойство в вашем компоненте bean definition -

<property name="packagesToScan" value="com.XX.XX.XX.XX" />

новое свойство, которое будет добавлено в этот блок -

<bean id="entityManagerFactory1" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
           <property name="persistenceUnitName" value="db2" />     
        <property name="dataSource" ref="dataSource1"/>
  <-- add here for both beans  -->

        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
                <property name="showSql" value="true"/>
                <property name="generateDdl" value="false"/>
                <property name="database" value="MYSQL" />
                <property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect"/>
            </bean>
        </property>
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect">
            </bean>
        </property>
    </bean>

Кроме того, вам не хватает свойства persistenceXmlLocation -

 <property name="persistenceXmlLocation" value="***/persistence.xml" />

Ответ 3

Сообщение об ошибке, которое вы указали, указывает, что вы используете autwiring по типу для объекта типа EntityManagerFactory. Ни один из кода, который вы пока показали, содержит такую инъекцию, которая подразумевает, что она, вероятно, в некотором коде, который вы еще не опубликовали.

Если бы вы отправили полную трассировку стека ошибки, вы могли бы подойти к стеку, чтобы увидеть, какой компонент содержит недопустимую ссылку на объект EntityManagerFactory, что, в свою очередь, позволит вам изменить то, как вы ссылаетесь он позволяет ссылаться на конкретный компонент, который вы хотите.

Обновить

Основываясь на дополнительной информации, которую вы предоставили (спасибо) и некоторым Google, похоже, что другие пользователи так же обнаружили, что указать unitName недостаточно, чтобы получить правильный EntityManager. Несколько сообщений в Интернете поддерживают @lucid рекомендацию использовать аннотацию @Qualifier, чтобы заставить Spring выбрать правильный компонент, но, к сожалению , аннотация была введена в 2.5, поэтому она доступна только вам, если вы обновляете. (Который, учитывая возраст рамки Spring, который вы используете, вероятно, хорошая идея, но это отдельный разговор.)

Тем не менее, несколько пользователей указали, что альтернативный подход доступен в 2.0.5, используя один PersistenceUnitManager, который ссылается на несколько источников данных, а не на несколько единиц сохранения, каждый из которых ссылается на один источник данных. Из официальных документов Spring: https://docs.spring.io/spring/docs/2.0.x/reference/orm.html#orm-jpa-multiple-pu.

В целом я предлагаю вам рассмотреть возможность обновления до версии Spring, которая не более десяти лет назад, что позволит вам указать аннотацию @Qualifier для ответа @lucid. Но если это невозможно по какой-либо причине, подход PersistenceUnitManager должен дать вам способ заставить его работать в Spring 2.0.5.

Ответ 4

Есть несколько вещей, которые мне не очень нравятся. Имя сортировщиков не будет соответствовать именам свойств, я думаю, что это важно, во-вторых, о наследовании, некоторые аннотации иногда работают только в конкретных классах, а не в базовых классах. Я попытался бы изменить базовый сервис, как следует.

public class BaseService {

@PersistenceContext(unitName = "db2")
private EntityManager em;

@PersistenceContext(unitName = "db1")
private EntityManager em1;

public EntityManager getEm() {
    return em;
}

protected EntityManager getEm2() {
    return em1;
}

public void setEm(EntityManager entityMgr) {
    this.em = entityMgr;
}

public void setEm1(EntityManager entityMgr) {
    this.em = entityMgr;
}
}

Если это не сработает, я, вероятно, попытаюсь удалить базовый класс и посмотреть, помещаю ли я аннотацию под конкретный класс, я могу сделать это, я бы сделал это, переместив только аннотации в класс RcAbcMaintenanceServiceImpl и удалив оператор расширения, который наследуется от базовый сервис

Кроме того, я заметил, что в аннотации PersistenceContext имеется еще один параметр https://docs.oracle.com/javaee/6/api/javax/persistence/PersistenceContext.html, поэтому вы можете попытаться использовать это имя, чтобы он соответствовал идентификатору определение компонента.