項目近期有個需求,要同時連接兩個數據庫,並且不同環境,數據庫種類還不同,我們有兩個數據庫,其中一個測試環境是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數據源,我就替換了一下,測試運行良好。