多个jpa:xml配置中的存储库,如何使用Spring java配置使用@EnableJPARepositories进行配置?

时间:2013-02-21 17:40:31

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

我已经研究过并发现explaination and sample code如何将spring数据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:仅存储库),并且不可能多次声明注释。

2 个答案:

答案 0 :(得分:18)

我创建了一个'minimal'多数据源项目来帮助我找出如何做到这一点。那里有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 config @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实体管理器连接。

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

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

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

 No bean named 'entityManagerFactory' is defined.

这是因为...... drumroll ...我们没有一个默认名称为“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存储库的工作应用程序来管理两个单独数据库中的实体。随意从上面的链接中获取项目并运行测试以查看这种情况。希望这个答案对更多的人有用,因为我花了相当多的时间来尽可能干净地使用尽可能少的代码来完成这项工作。欢迎任何改进答案或演示项目的想法。

答案 1 :(得分:0)

您可以尝试将其放在两个@Configuration课程中(每个@EnableJpaRepositories一个@Configuration)。