如何使用 Spring 和 JPA 设置多个数据源

How to setup multiple data sources with Spring and JPA

在我们的应用程序中,我们希望使用 Spring 和 JPA 设置多个数据源。因此我们创建了 2 个 entityManagerFactory、2 个数据源和 2 个事务管理器。

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>

我还向 b_spring.xml.

声明了另一个 entityManagetFactory、Transaction Manager 和 dataSource

错误

Initialization of bean failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [javax.persistence.EntityManagerFactory] is defined: expected single bean but found 2 Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [javax.persistence.EntityManagerFactory] is defined: expected single bean but found 2 at org.springframework.beans.factory.BeanFactoryUtils.beanOfTypeIncludingAncestors(BeanFactoryUtils.java:303) at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findDefaultEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:451) at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:428) at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$AnnotatedMember.resolveEntityManager(PersistenceAnnotationBeanPostProcessor.java:582) at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$AnnotatedMember.resolve(PersistenceAnnotationBeanPostProcessor.java:553) at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$AnnotatedMember.inject(PersistenceAnnotationBeanPostProcessor.java:489)

有几件事对我来说不太好。 setter 的名称与 属性 名称不匹配,我认为这很重要,第二件事是关于继承,一些注释有时只适用于具体 classes 而不是基础 class是的。我会尝试如下更改基本服务。

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;
}
}

如果这不起作用,我可能会尝试移除基础 class,看看我是否将注释放在混凝土下面 class 我可以让它起作用,我会这样做,只移动RcAbcMaintenanceServiceImpl class 的注释并删除继承自 BaseService

的扩展语句

此外,我注意到 PersistenceContext 注释有另一个参数 https://docs.oracle.com/javaee/6/api/javax/persistence/PersistenceContext.html,因此您也可以尝试使用名称来匹配 bean 定义中的 id。

在多个数据源配置的情况下,我们需要定义哪个将被视为主要数据源。我们可以指定在 java 配置中使用 @Primary 注释或在 XML bean 配置中使用 primary=true

由于在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"/>

您是否尝试提供包含您的 EntityManagerFactory bean 的包详细信息?

您可以在 bean 定义中以 属性 形式提供包详细信息 -

<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" />

您 post 编辑的错误消息表明您正在按类型为类型为 EntityManagerFactory 的对象自动装配。 None 您目前展示的代码中包含此类注入,这意味着它可能在您尚未 post 编辑的某些代码中。

如果您要 post 错误的完整堆栈跟踪,您将能够向上遍历堆栈以查看哪个 bean 包含对 EntityManagerFactory 对象的不可满足引用,而该对象又是会让您更改引用它的方式,以允许引用您想要的特定 bean。

更新

根据您提供的更多信息(谢谢)和一些谷歌搜索,似乎其他用户也类似地发现指定 unitName 不足以注入正确的 EntityManager。几个 post 在线备份 @lucid 的建议使用 @Qualifier 注释来获得 Spring 到 select 正确的 bean,但不幸的是 that annotation was introduced in 2.5 所以它只对你可用你升级。 (考虑到您使用的 Spring 框架的年龄,这可能是个好主意,但这是一个单独的话题。)

然而,several users have indicated that an alternate approach is available in 2.0.5, using a single PersistenceUnitManager that references multiple data sources rather than multiple persistence units that each reference a single data source. From the official Spring docs: https://docs.spring.io/spring/docs/2.0.x/reference/orm.html#orm-jpa-multiple-pu.

总的来说,我建议您考虑升级到不超过十年的 Spring 版本,这样您就可以根据 @lucid 的回答指定一个 @Qualifier 注释。但如果由于某种原因这不可能,PersistenceUnitManager 方法应该为您提供一种使其在 Spring 2.0.5.

中工作的方法