使用 Spring Data JPA 的多个数据库

Posted

技术标签:

【中文标题】使用 Spring Data JPA 的多个数据库【英文标题】:multiple databases with Spring Data JPA 【发布时间】:2012-04-11 04:55:05 【问题描述】:

我正在尝试在项目中使用带有 2 个数据库的 Spring Data JPA。但是当我尝试运行应用程序时会触发异常:

07:21:47.734 [main] ERROR o.s.web.context.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'deviceRepository': Injection of persistence dependencies 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
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:342) ~[spring-orm-3.1.0.RELEASE.jar:3.1.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1106) ~[spring-beans-3.1.0.RELEASE.jar:3.1.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-3.1.0.RELEASE.jar:3.1.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456) ~[spring-beans-3.1.0.RELEASE.jar:3.1.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294) ~[spring-beans-3.1.0.RELEASE.jar:3.1.0.RELEASE]
...

这是我的 applicationContext.xml

<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource1">
    <property name="driverClassName" value="$database.driverClassName"/>
    <property name="url" value="$database.url1"/>
    <property name="username" value="$database.username1"/>
    <property name="password" value="$database.password1"/>
    <property name="testOnBorrow" value="true"/>
    <property name="testOnReturn" value="true"/>
    <property name="testWhileIdle" value="true"/>
    <property name="timeBetweenEvictionRunsMillis" value="1800000"/>
    <property name="numTestsPerEvictionRun" value="3"/>
    <property name="minEvictableIdleTimeMillis" value="1800000"/>
    <property name="validationQuery" value="SELECT 1"/>
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager1">
    <property name="entityManagerFactory" ref="entityManagerFactory1"/>
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager1"/>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory1">
    <property name="persistenceUnitName" value="persistenceUnit1"/>
    <property name="dataSource" ref="dataSource1"/>
</bean>

<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource2">
    <property name="driverClassName" value="$database.driverClassName"/>
    <property name="url" value="$database.url2"/>
    <property name="username" value="$database.username2"/>
    <property name="password" value="$database.password2"/>
    <property name="testOnBorrow" value="true"/>
    <property name="testOnReturn" value="true"/>
    <property name="testWhileIdle" value="true"/>
    <property name="timeBetweenEvictionRunsMillis" value="1800000"/>
    <property name="numTestsPerEvictionRun" value="3"/>
    <property name="minEvictableIdleTimeMillis" value="1800000"/>
    <property name="validationQuery" value="SELECT 1"/>
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager2">
    <property name="entityManagerFactory" ref="entityManagerFactory2"/>
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager2"/>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory2">
    <property name="persistenceUnitName" value="persistenceUnit2"/>
    <property name="dataSource" ref="dataSource2"/>
</bean>

这是我的 DAO 界面:

@Repository
public interface DeviceRepository extends JpaRepository<Device, DevicePK>,
    JpaSpecificationExecutor<Device> 

我已经阅读了很多关于 @PersistenceContext 的内容,但我从未见过 JpaRepository 的用法。

【问题讨论】:

【参考方案1】:

您确实有 2 个实体管理器。 我从未使用过 JPARepository,但如果它在 nosql 的 spring-data 上的对应部分附近工作,Spring 将增强该类,并可能在其上注入 EM。您的问题是您声明了 2 个 EM。

查看 spring-jpa 文档,它们将向您展示如何配置存储库,以了解如何将特定 EMF 添加到您的存储库中

【讨论】:

【参考方案2】:

长话短说:

您必须创建该接口的自定义实现,并且该实现必须包含声明为的实体管理器:

    @PersistenceContext(unitName = "persistenceUnit1")
    private EntityManager entityManager;

如果您想使用持久性单元 1。

检查一下:http://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/repositories.html#repositories.custom-implementations,它也有示例,示例 1.17(页面下方)实现了接口并在其中包含实体管理器,并且该实体管理器被传递给超级构造函数。

您还可以查看Spring Data - JPA, customizing Repository not working,它具有扩展 JpaRepository 的接口实现,并且它还具有在实现中声明的实体管理器。只需将 unitName 添加到 @PersistenceContext 注释并尝试这种方式。

您可能不需要任何自定义方法(因此您的界面可以为空),但您确实需要传递实体管理器的构造函数以及所有修改扩展您自己的界面以绕过默认自动连接行为的东西。

【讨论】:

【参考方案3】:

必须将两个数据源之一定义为主数据源。

&lt;bean&gt; 有一个可以设置为 true 或 false 的主要属性:

&lt;bean primary="true|false"/&gt;

通常在@Configuration 中,@Primary 注释被放置到:

EntityManager DataSource TransactionManager

所以你可以尝试添加primary="true"

到以下 bean:

dataSource1 transactionManager1 entityManagerFactory1

【讨论】:

以上是关于使用 Spring Data JPA 的多个数据库的主要内容,如果未能解决你的问题,请参考以下文章

多个数据库的 Spring Data JPA 配置

多个数据库使用相同的域模型和 Spring Boot 和 Spring Data JPA

多租户:使用 Spring Data JPA 管理多个数据源

管理来自多个表的数据 Spring data JPA

带有 Spring Boot 的 Spring Data JPA 中的多个数据源,[重复]

将多个数据库与 Spring Data JPA 一起使用时,不会发生名为“ConfigurationClassPostProcessor.importRegistry”的 bean 异常