【问题标题】:Multiple jpa:repositories in xml config, how to configure with @EnableJPARepositories using Spring java config?xml配置中的多个jpa:repositories,如何使用Spring java config配置@EnableJPARepositories?
【发布时间】:2013-02-07 03:55:34
【问题描述】:

我研究并发现了一个explaination and sample code关于如何将spring data jpa与多个数据源一起使用,它是指在xml配置中配置多个jpa:repositories,如下所示:

<jpa:repositories base-package="org.springframework.data.jpa.repository.sample"
    entity-manager-factory-ref="entityManagerFactory">
    <repository:exclude-filter type="assignable" expression="org.springframework.data.jpa.repository.sample.AuditableUserRepository" />
</jpa:repositories>
<jpa:repositories base-package="org.springframework.data.jpa.repository.sample"
    entity-manager-factory-ref="entityManagerFactory-2"
    transaction-manager-ref="transactionManager-2">
    <repository:include-filter type="assignable" expression="org.springframework.data.jpa.repository.sample.AuditableUserRepository" />
</jpa:repositories>

您将如何使用 java 配置和 @EnableJpaRepositories 注释声明上述两个 jpa:repositories 配置?

注解似乎只支持一组属性(即仅适用于一个 jpa:repository),并且无法多次声明该注解。

【问题讨论】:

    标签: java spring spring-data spring-data-jpa


    【解决方案1】:

    我创建了一个“最小”的多数据源项目来帮助我弄清楚如何做到这一点。那里有 7 个 Java 类和其他配置,所以我只会在这个答案中发布关键摘录。您可以从 GitHub 获取完整的项目:https://github.com/gratiartis/multids-demo

    该演示设置了两个 JPA 实体:

    @Entity public class Foo { /* Constructors, fields and accessors/mutators */ }
    @Entity public class Bar { /* Constructors, fields and accessors/mutators */ }
    

    与这些相关联,我们将创建两个存储库。感谢 Spring Data 的强大功能,我们可以完全通过定义扩展 JpaRepository 的接口来获得一些功能齐全的存储库:

    public interface FooRepository extends JpaRepository<Foo, Long> {}
    public interface BarRepository extends JpaRepository<Bar, Long> {}
    

    现在我们需要确保每个都映射到自己数据库中的表。

    为了实现这一点,我们需要两个独立的实体管理器,每个管理器都有不同的数据源。但是,在 Spring Java 配置 @Configuration 类中,我们只能有一个 @EnableJpaRepositories 注释,并且每个这样的注释只能引用一个 EntityManagerFactory。为此,我们创建了两个独立的@Configuration 类:FooConfig 和 BarConfig。

    每个@Configuration 类都将定义一个基于嵌入式 HSQL 数据库的 DataSource:

    @Bean(name = "fooDataSource")
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
                .setName("foodb").setType(EmbeddedDatabaseType.HSQL).build();
    }
    @Bean(name = "barDataSource")
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
                .setName("bardb").setType(EmbeddedDatabaseType.HSQL).build();
    }
    
    @Bean(name = "barEntityManagerFactory")
    public EntityManagerFactory entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean lef = 
                new LocalContainerEntityManagerFactoryBean();
        lef.setDataSource(dataSource());
        lef.setJpaVendorAdapter(jpaVendorAdapter);
        lef.setPackagesToScan("com.sctrcd.multidsdemo.domain.bar");
        lef.setPersistenceUnitName("barPersistenceUnit");
        lef.afterPropertiesSet();
        return lef.getObject();
    }
    @Bean(name = "fooEntityManagerFactory")
    public EntityManagerFactory entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean lef = 
                new LocalContainerEntityManagerFactoryBean();
        lef.setDataSource(dataSource());
        lef.setJpaVendorAdapter(jpaVendorAdapter);
        lef.setPackagesToScan("com.sctrcd.multidsdemo.domain.foo");
        lef.setPersistenceUnitName("fooPersistenceUnit");
        lef.afterPropertiesSet();
        return lef.getObject();
    }
    

    每个配置都应该定义一个EntityManagerFactory,如上所述,它引用自己的dataSource() @Bean 方法。它还定义了它管理的 @Entity bean 的路径。您需要确保不同数据源的 @Entity bean 位于不同的包中。

    此时值得注意的是,如果这些配置中的每一个都使用关键持久性 bean 的默认命名(即 entityManagerFactory),那么 Spring 将看到有两个具有 EntityManager 接口的 bean,它们都具有相同的名称。所以会选择一个。这会导致错误,例如:

    Not an managed type: class com.sctrcd.multidsdemo.domain.bar.Bar
    

    这可以在此处的演示项目的分支中看到:https://github.com/gratiartis/multids-demo/tree/1-unnamed-entitymanager-beans

    这是因为在该示例中,Spring 已经连接了与“foodb”数据库相关的 bean,而 Bar 不是该数据库中的实体。不幸的是,BarRepository 已与 Foo 实体管理器连接起来。

    我们通过在每个配置类中命名所有 bean 来解决这个问题。即

    @Bean(name = "fooDataSource") public DataSource dataSource() { .. }
    @Bean(name = "fooEntityManager") public EntityManager entityManager() { .. }
    

    此时如果您要在项目中运行测试,您可能会看到如下警告:

     No bean named 'entityManagerFactory' is defined.
    

    这是因为...鼓声...我们没有默认名称为“entityManagerFactory”的 EntityManagerFactory。我们有一个叫做“fooEntityManagerFactory”,另一个叫做“barEntityManagerFactory”。 Spring 正在寻找具有默认名称的东西,因此我们需要指示它以不同的方式连接。

    事实证明,这非常简单。我们只需在每个@Configuration 类的@EnableJpaRepositories 注解中放置正确的引用即可。

    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(
            entityManagerFactoryRef = "fooEntityManagerFactory", 
            transactionManagerRef = "fooTransactionManager",
            basePackages = {"com.sctrcd.multidsdemo.integration.repositories.foo"})
    public class FooConfig {
        // ...
    }
    
    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(
            entityManagerFactoryRef = "barEntityManagerFactory", 
            transactionManagerRef = "barTransactionManager",
            basePackages = { "com.sctrcd.multidsdemo.integration.repositories.bar" })
    public class BarConfig {
        // ...
    }
    

    如您所见,这些@EnableJpaRepositories 注释中的每一个都定义了一个特定的命名EntityManagerFactory 和PlatformTransactionManager。他们还指定哪些存储库应与这些 bean 连接。在示例中,我将存储库放在特定于数据库的包中。也可以按名称定义每个单独的存储库,通过将 includeFilters 添加到注释中,但是通过按数据库隔离存储库,我相信事情最终应该更具可读性。

    此时,您应该有一个使用 Spring Data 存储库来管理两个独立数据库中的实体的工作应用程序。随意从上面的链接中获取项目并运行测试以查看这种情况。希望这个答案对更多的人有用,因为我已经花了相当多的时间来尽可能干净地使用尽可能少的代码来完成这项工作。欢迎任何改进答案或演示项目的想法。

    【讨论】:

      【解决方案2】:

      您可以尝试将它放在两个 @Configuration 类上(每个 @Configuration 一个 @EnableJpaRepositories)。

      【讨论】:

        猜你喜欢
        • 2015-08-23
        • 2017-01-06
        • 2015-12-04
        • 2012-07-01
        • 2014-12-06
        • 2023-03-21
        • 2014-05-21
        • 2019-12-23
        • 2015-11-19
        相关资源
        最近更新 更多