http://tommwq.tech/blog/2020/12/04/262
在Spring JPA中配置多個數據源並不複雜,只需要爲每個數據源聲明一個@EnableJpaRepositories。
如果程序只使用一個數據源,通常在入口類添加註解@EnableJpaRepositories,不需要任何參數。然後寫好配置spring.datasource.*就可以了。但是如果需要多個數據源,就要爲每個數據源聲明一個@EnableJpaRepositories。@EnableJpaRepositoreis根據包名進行掃描,因此不同數據源使用的Entity和Repository需要分別保存到不同的包中。爲了便於管理,建議爲@EnableJpaRepositories編寫一個單獨的配置類,和Entity、Repository放在同一個包裏。
com.tommwq.test.foo FooEntity FooRepository FooConfiguration com.tommwq.test.bar BarEntity BarRepository BarConfiguration
使用多個數據源時,Entity和Repository不用修改。只要設置好@EnableJpaRepositories,就能讓Repository找到正確的數據源,自動裝配。其中主要的兩個參數是創建Entity的entityManagerFactoryRef和創建Repository的transactionManagerRef。爲了創建EntityManagerFactory,我們需要藉助DataSourceProperties創建DataSource對象。而使用@ConfigurationProperties,可以自動從配置生成DataSourceProperties。創建TransactionManager則需要指定SQL方言。方言類名可以通過Environment從配置中讀取。
package com.tommwq.test.foo; import javax.sql.*; import org.apache.commons.dbcp2.BasicDataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; import org.springframework.core.env.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.vendor.*; import org.springframework.transaction.PlatformTransactionManager; @Configuration @EnableJpaRepositories entityManagerFactoryRef="fooEntityManagerFactory", transactionManagerRef="fooTransactionManager") public class FooConfiguration { @Autowired Environment env; @Bean @ConfigurationProperties(prefix="foo.datasource") public DataSourceProperties fooDataSourceProperties() { return new DataSourceProperties(); } @Bean public DataSource fooDataSource() { return fooDataSourceProperties() .initializeDataSourceBuilder() .type(BasicDataSource.class) // 使用了 apache DBCH2 .build(); } @Bean(name="fooEntityManagerFactory") public LocalContainerEntityManagerFactoryBean fooEntityManagerFactory(EntityManagerFactoryBuilder builder) { return builder.dataSource(fooDataSource()) .packages("com.tommwq.test.foo") .build(); } @Bean(name="fooTransactionManager") public PlatformTransactionManager fooTransactionManager(@Qualifier("fooEntityManagerFactory") LocalContainerEntityManagerFactoryBean bean) { JpaTransactionManager manager = new JpaTransactionManager(bean.getObject()); HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); vendorAdapter.setDatabasePlatform(env.getProperty("foo.datasource.dialect")); manager.setJpaDialect(vendorAdapter.getJpaDialect()); return manager; } }
這就是一個數據源配置類的完整代碼。每個數據源都要寫一個這樣的類。 其中一個配置類中的Bean要標記爲@Primary。 代碼看起來很多,實際上大部分是固定的,可以用模板來生成,變化的只有3個參數:
packageName dataSourceName ConfigurationClassName
下面是採用FreeMarker編寫的模板。
package ${packageName}; import javax.sql.*; import org.apache.commons.dbcp2.BasicDataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; import org.springframework.core.env.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.vendor.*; import org.springframework.transaction.PlatformTransactionManager; @Configuration @EnableJpaRepositories entityManagerFactoryRef="${dataSourceName}EntityManagerFactory", transactionManagerRef="${dataSourceName}TransactionManager") public class ${configurationClassName} { @Autowired Environment env; @Bean @ConfigurationProperties(prefix="${dataSourceName}.datasource") public DataSourceProperties ${dataSourceName}DataSourceProperties() { return new DataSourceProperties(); } @Bean public DataSource ${dataSourceName}DataSource() { return ${dataSourceName}DataSourceProperties() .initializeDataSourceBuilder() .type(BasicDataSource.class) // 使用了 apache DBCH2 .build(); } @Bean(name="${dataSourceName}EntityManagerFactory") public LocalContainerEntityManagerFactoryBean ${dataSourceName}EntityManagerFactory(EntityManagerFactoryBuilder builder) { return builder.dataSource(${dataSourceName}DataSource()) .packages("${packageName}") .build(); } @Bean(name="${dataSourceName}TransactionManager") public PlatformTransactionManager ${dataSourceName}TransactionManager(@Qualifier("${dataSourceName}EntityManagerFactory") LocalContainerEntityManagerFactoryBean bean) { JpaTransactionManager manager = new JpaTransactionManager(bean.getObject()); HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); vendorAdapter.setDatabasePlatform(env.getProperty("${dataSourceName}.datasource.dialect")); manager.setJpaDialect(vendorAdapter.getJpaDialect()); return manager; } }
foo.datasource.driverClassName=com.mysql.jdbc.Driver foo.datasource.url=jdbc:mysql://localhost:3306/test foo.datasource.username=root foo.datasource.password=myPassword foo.datasource.dialect=org.hibernate.dialect.MySQL5InnoDBDialect