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