我创建了一个“最小”的多数据源项目来帮助我弄清楚如何做到这一点。那里有 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 来解决这个问题。IE
@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 存储库来管理两个独立数据库中的实体的工作应用程序。随意从上面的链接中获取项目并运行测试以查看这种情况。希望这个答案对更多人有用,因为我已经花费了大量时间来尽可能干净地使用尽可能少的代码来完成这项工作。欢迎任何改进答案或演示项目的想法。