我創建了一個'最小'多個數據源項目來幫助我解決如何做到這一點。這裏有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 */ }
結合這些,我們將創建兩個資源庫。
public interface FooRepository extends JpaRepository<Foo, Long> {}
public interface BarRepository extends JpaRepository<Bar, Long> {}
現在,我們需要確保這些映射到表在自己的數據庫:由於春季數據迷死,我們可以通過定義擴展JpaRepository接口讓自己一些漂亮的全功能庫純粹。
爲了實現這一點,我們需要兩個獨立的實體管理器,每個實體管理器都有不同的數據源。但是,在Spring Java配置@Configuration
類中,我們只能有一個@EnableJpaRepositories
註釋,每個這樣的註釋只能引用一個EntityManagerFactory。爲了達到這個目的,我們創建了兩個單獨的@Configuration
類:FooConfig和BarConfig。
每個@Configuration類將定義基於嵌入式HSQL數據庫上一個數據源:
@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,它們都具有相同的名稱。所以會選擇一個。這會導致錯誤,如:https://github.com/gratiartis/multids-demo/tree/1-unnamed-entitymanager-beans
這是因爲在這個例子中,春天已經有線了有關「foodb豆:
Not an managed type: class com.sctrcd.multidsdemo.domain.bar.Bar
這可以在演示項目這裏的分支可以看出「數據庫,而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存儲庫的工作應用程序來管理兩個單獨數據庫中的實體。隨意從上面的鏈接中抓取項目並運行測試以查看發生的情況。希望這個答案對更多人來說是有用的,因爲我花了相當多的時間用盡可能少的代碼儘可能乾淨地做到這一點。歡迎任何關於改進答案或演示項目的想法。