项目近期有个需求,要同时连接两个数据库,并且不同环境,数据库种类还不同,我们有两个数据库,其中一个测试环境是oracle,生产环境又是postgre,另一个一直是postgre,调查了网上能搜到很多资料,基本上写法都一致,比如https://blog.csdn.net/itguangit/article/details/78747969,但用在sb 2.3上会发现两个问题,一个是EntityManagerFactoryBuilder在启动时无法自动注入,虽然理论上sb会自动创建其实例,另一个是2.3中jpaProperties已经没有getHibernateProperties这个方法了,花了两天时间,发现一个外国人写的帖子方法是适用于2.3这个版本的,发布时间是去年,比较新,原帖见https://springframework.guru/how-to-configure-multiple-data-sources-in-a-spring-boot-application/ ,但也不是没有问题,需要再改造一下才能完全适应项目开发,下面是改造后的大致的代码
- application.yml
spring: datasource: first: url: jdbc:postgresql://localhost:5432/sample driverClassName: org.postgresql.Driver username: postgres password: 111 second: url: jdbc:oracle:thin:@//192.168.1.100:1521/orclpdb driverClassName: oracle.jdbc.OracleDriver username: webdba password: 111 jpa: hibernate: ddl-auto: none show-sql: true
- 数据源1的配置文件,postgre db
需要注意的是,这里的每个方法都要使用Primary的注解,只有一个配置文件需要如此 ,这个是sb规定,否则启动时会报错,貌似任意一个都可以指定成这样的,不要被字面意思迷惑package com.xxx.config; import com.zaxxer.hikari.HikariDataSource; 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.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; @Configuration @EnableTransactionManagement @EnableJpaRepositories(basePackages = "com.xxx.repo.first", //当前数据源对应的repository的包名,每个数据源应该有独立的包 entityManagerFactoryRef = "firstEntityManagerFactory", transactionManagerRef= "firstTransactionManager" ) public class FirstJpaConfig { @Bean @Primary @ConfigurationProperties("spring.datasource.first") public DataSourceProperties firstDataSourceProperties() { return new DataSourceProperties(); } @Bean @Primary @ConfigurationProperties("spring.datasource.first.configuration") public DataSource firstDataSource() { return firstDataSourceProperties().initializeDataSourceBuilder() .type(HikariDataSource.class).build(); } @Primary @Bean(name = "firstEntityManagerFactory") public LocalContainerEntityManagerFactoryBean firstEntityManagerFactory(EntityManagerFactoryBuilder builder) { return builder .dataSource(firstDataSource()) .packages("com.xxx.entity.first") //当前数据源对应的实体的包名,每个数据源应该有独立的包 .build(); } @Primary @Bean public PlatformTransactionManager firstTransactionManager( final @Qualifier("firstEntityManagerFactory") LocalContainerEntityManagerFactoryBean firstEntityManagerFactory) { return new JpaTransactionManager(firstEntityManagerFactory.getObject()); } @Primary @Bean(name = "firstEntityManager") //这个不是必须,但如果你想在service中注入一个EntityManager用于执行动态jql,这个就是必要的,否则,sb会不知道选择哪个EntityManager实例使用,每个数据源对应一个实例 public EntityManager entityManager(@Qualifier("firstEntityManagerFactory") EntityManagerFactory factory) { return factory.createEntityManager(); } }
- 数据源2的配置文件,oracle db,和第一个高度类似,不解释了
package com.xxx.config; import com.zaxxer.hikari.HikariDataSource; 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.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; @Configuration @EnableTransactionManagement @EnableJpaRepositories(basePackages = "com.xxx.repo.second", entityManagerFactoryRef = "secondEntityManagerFactory", transactionManagerRef= "secondTransactionManager" ) public class SecondJpaConfig { @Bean @ConfigurationProperties("spring.datasource.second") public DataSourceProperties secondDataSourceProperties() { return new DataSourceProperties(); } @Bean @ConfigurationProperties("spring.datasource.second.configuration") public DataSource secondDataSource() { return secondDataSourceProperties().initializeDataSourceBuilder() .type(HikariDataSource.class).build(); } @Bean(name = "secondEntityManagerFactory") public LocalContainerEntityManagerFactoryBean secondEntityManagerFactory(EntityManagerFactoryBuilder builder) { return builder .dataSource(secondDataSource()) .packages("com.xxx.entity.second") .build(); } @Bean public PlatformTransactionManager secondTransactionManager( final @Qualifier("secondEntityManagerFactory") LocalContainerEntityManagerFactoryBean secondEntityManagerFactory) { return new JpaTransactionManager(secondEntityManagerFactory.getObject()); } @Bean(name = "secondEntityManager") public EntityManager entityManager(@Qualifier("secondEntityManagerFactory") EntityManagerFactory factory) { return factory.createEntityManager(); } }
原帖中使用的数据源实现类是dbcp,听说目前性能最好的是Hikari数据源,我就替换了一下,测试运行良好。