SpringBoot多資料庫連線(mysql+oracle)
阿新 • • 發佈:2020-07-24
出於業務需求,有時我們需要在spring boot web應用程式中配置多個數據源並連線到多個數據庫。
使用過Spring Boot框架的小夥伴們,想必都發現了Spring Boot對JPA提供了非常好的支援,在開發過程中可以很簡潔的程式碼輕鬆訪問資料庫,獲取我們想要的資料。
因此在這裡,使用Spring Boot和JPA配置多個數據源的場景。
作者:吳家二少 部落格地址:https://www.cnblogs.com/cloudman-open/ 本文歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線
專案配置
在本文中,主要使用兩個不同的資料庫,分別為:- mysql(springboot)【primary,優先搜尋該資料庫】:mysql資料庫,包含User的資訊
- oracle(springboot): oracle資料庫, 包含Country資訊
專案依賴
為了支援Mysql和Oracle資料庫,我們必須要在pom.xml檔案中新增相應的依賴。<dependencies> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>11.2.0.3.0</version> <scope>compile</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>javax.persistence</groupId> <artifactId>javax.persistence-api</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> </dependencies>
包管理
為了方便程式碼的開發,我們將mysql和oracle分開放在兩個不同的package下面,具體的包結構如下: 將不同的模型分開放入mysql和oracle包目錄下,需要注意的是,mysql和oracle為兩個不同的資料庫,所以兩個資料庫中可能存在某個表名稱一致的場景。該場景下,會優先匹配primary的資料庫,如果該資料庫down了,才會匹配另外一張表。所以,如果想要兩張表都正常使用,建議使用不同的Entity名稱。資料庫連線配置
我們在屬性檔案application.properties中分別配置兩個單獨的jdbc連線,將所有關聯的Entity類和Repository對映到兩個不同的包中。## jdbc-primary spring.datasource.url=jdbc:mysql://localhost:33306/springboot?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false spring.datasource.username=springboot spring.datasource.password=123456 spring.ds_mysql.driverClassName=com.mysql.jdbc.Driver ## jdbc-second spring.second.datasource.url=jdbc:oracle:thin:@localhost:1909/xxx.xxx.com spring.second.datasource.userName=springboot spring.second.datasource.password=123456 spring.second.datasource.driver-class-name=oracle.jdbc.OracleDriver ## jpa spring.jpa.hibernate.ddl-auto=none spring.jpa.show-sql=true spring.jpa.properties.hibernate.jdbc.time_zone=UTC spring.jpa.properties.hibernate.jdbc.fetch_size=500 spring.jpa.properties.hibernate.jdbc.batch_size=100
資料來源配置
需要注意的是,在配置多個數據源期間,必須將其中一個數據源標記為primary,否則Spring Boot會檢測到多個型別的資料來源,從而無法正常啟動。定義Data Source的Bean
想要建立Data Source,我們必須先例項化org.springframework.boot.autoconfigure.jdbc.DataSourceProperties類,載入application.properties檔案中配置的資料庫連線資訊,並通過DataSourceProperties物件的初始化builder方法建立一個javax.sql.DataSource物件。 primary Data Source@Primary @Bean(name = "mysqlDataSourceProperties") @ConfigurationProperties("spring.datasource") public DataSourceProperties dataSourceProperties() { return new DataSourceProperties(); } @Primary @Bean(name = "mysqlDataSource") @ConfigurationProperties("spring.datasource.configuration") public DataSource dataSource (@Qualifier("mysqlDataSourceProperties") DataSourceProperties mysqlDataSourceProperties) { return mysqlDataSourceProperties.initializeDataSourceBuilder() .type(HikariDataSource.class) .build(); }Secondary Data Source
@Bean(name = "oracleDataSourceProperties") @ConfigurationProperties("spring.second.datasource") public DataSourceProperties dataSourceProperties() { return new DataSourceProperties(); } @Bean @ConfigurationProperties("spring.second.datasource.configuration") public DataSource oracleDataSource(@Qualifier("oracleDataSourceProperties") DataSourceProperties oracleDataSourceProperties) { return oracleDataSourceProperties.initializeDataSourceBuilder() .type(HikariDataSource.class) .build(); }
我們使用@Qualifier註解,自動關聯指定的DataSourceProperties.
定義實體類管理工廠的Bean 上面說了,應用程式使用Spring Data JPA的repository介面將我們從實體管理器(Entity Manager)中抽象出來,從而進行資料的訪問。這裡,我們使用org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean這個Bean來建立EM例項,後面利用這個EM例項與JPA entities進行互動。 由於我們這裡有兩個資料來源,所以我要為每個資料來源單獨建立一個EntityManagerFactory。 Primary Entity Manager Factory@Primary @Bean(name = "mysqlEntityManagerFactory") public LocalContainerEntityManagerFactoryBean entityManagerFactory( EntityManagerFactoryBuilder builder, @Qualifier("mysqlDataSource") DataSource mysqlDataSource) { return builder.dataSource(mysqlDataSource) .packages("com.example.demo.model.mysql") .persistenceUnit("mysql") .build(); }Secondary Entity Manager Factory
@Bean(name = "oracleEntityManagerFactory") public LocalContainerEntityManagerFactoryBean oracleEntityManagerFactory( EntityManagerFactoryBuilder builder, @Qualifier("oracleDataSource") DataSource oracleDataSource) { return builder.dataSource(oracleDataSource) .packages("com.example.demo.model.oracle") .persistenceUnit("oracle") .build(); }我們使用@Qualifie註解,自動將DataSource關聯到對應的EntityManangerFactory中。 在這裡我們可以分別配置實體類管理工廠所管理的packages,為了方便開發和閱讀,分別將mysql和oracle關聯的實體類放在對應的目錄下。
事務管理
我們為每個資料庫建立一個JPA事務管理器。 檢視原始碼我們可以發現JPA事務管理器需要EntityManangerFactory作為入參,所以利用上述定義的EntityMangerFactory分別生成對應的JPA事物管理器。 原始碼:public JpaTransactionManager(EntityManagerFactory emf) { this(); this.entityManagerFactory = emf; this.afterPropertiesSet(); }Primary transaction manager
@Primary @Bean(name = "mysqlTransactionManager") public PlatformTransactionManager mysqlTransactionManager(final @Qualifier("mysqlEntityManagerFactory") LocalContainerEntityManagerFactoryBean mysqlEntityManagerFactory) { return new JpaTransactionManager(mysqlEntityManagerFactory.getObject()); }Secondary transaction manager
@Bean(name = "oracleTransactionManager") public PlatformTransactionManager oracleTransactionManager( final @Qualifier("oracleEntityManagerFactory") LocalContainerEntityManagerFactoryBean oracleEntityManagerFactory) { return new JpaTransactionManager(oracleEntityManagerFactory.getObject()); }
JPA Repository配置
由於我們使用了兩個不同的資料來源,所以我們必須使用@EnableJpaRepositories註解為每個資料來源提供特定的資訊。 進入該註解原始碼,我們可以發現預設值如下:/** * Annotation to enable JPA repositories. Will scan the package of the annotated configuration class for Spring Data * repositories by default. * * @author Oliver Gierke * @author Thomas Darimont */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(JpaRepositoriesRegistrar.class) public @interface EnableJpaRepositories { /** * Base packages to scan for annotated components. {@link #value()} is an alias for (and mutually exclusive with) this * attribute. Use {@link #basePackageClasses()} for a type-safe alternative to String-based package names. */ String[] basePackages() default {}; /** * Configures the name of the {@link EntityManagerFactory} bean definition to be used to create repositories * discovered through this annotation. Defaults to {@code entityManagerFactory}. * * @return */ String entityManagerFactoryRef() default "entityManagerFactory"; /** * Configures the name of the {@link PlatformTransactionManager} bean definition to be used to create repositories * discovered through this annotation. Defaults to {@code transactionManager}. * * @return */ String transactionManagerRef() default "transactionManager"; }這裡僅列了一些我們關心的方法。 從原始碼我中我們可以看見,
- basePackages: 使用此欄位設定Repository的基本包,必須指向軟體包中repository所在目錄。
- entityManagerFactoryRef:使用此欄位引用預設或自定義的Entity Manager Factory, 這裡通過Bean的名稱進行指定, 預設Bean為entityManagerFactory。
- transactionManagerRef:使用此欄位引用預設或自定義的事務管理器,這裡通過Bean的名稱進行指定,預設Bean為transactionManager。
@Configuration @EnableTransactionManagement @EnableJpaRepositories(basePackages = {"com.example.demo.repository.mysql"}, entityManagerFactoryRef = "mysqlEntityManagerFactory", transactionManagerRef = "mysqlTransactionManager") public class MysqlDataSourceConfiguration { ... }secondary
@Configuration @EnableTransactionManagement @EnableJpaRepositories(basePackages = "com.example.demo.repository.oracle", entityManagerFactoryRef = "oracleEntityManagerFactory", transactionManagerRef = "oracleTransactionManager") public class OracleDataSourceConfiguration { ... }
完整的配置檔案
primary@Configuration @EnableTransactionManagement @EnableJpaRepositories(basePackages = {"com.example.demo.repository.mysql"}, entityManagerFactoryRef = "mysqlEntityManagerFactory", transactionManagerRef = "mysqlTransactionManager") public class MysqlDataSourceConfiguration { @Primary @Bean(name = "mysqlDataSourceProperties") @ConfigurationProperties("spring.datasource") public DataSourceProperties dataSourceProperties() { return new DataSourceProperties(); } @Primary @Bean(name = "mysqlDataSource") @ConfigurationProperties("spring.datasource.configuration") public DataSource dataSource (@Qualifier("mysqlDataSourceProperties") DataSourceProperties mysqlDataSourceProperties) { return mysqlDataSourceProperties.initializeDataSourceBuilder() .type(HikariDataSource.class) .build(); } @Primary @Bean(name = "mysqlEntityManagerFactory") public LocalContainerEntityManagerFactoryBean entityManagerFactory( EntityManagerFactoryBuilder builder, @Qualifier("mysqlDataSource") DataSource mysqlDataSource) { return builder.dataSource(mysqlDataSource) .packages("com.example.demo.model.mysql") .persistenceUnit("mysql") .build(); } @Primary @Bean(name = "mysqlTransactionManager") public PlatformTransactionManager transactionManager(final @Qualifier("mysqlEntityManagerFactory") LocalContainerEntityManagerFactoryBean mysqlEntityManagerFactory) { return new JpaTransactionManager(mysqlEntityManagerFactory.getObject()); } }secondary
@Configuration @EnableTransactionManagement @EnableJpaRepositories(basePackages = "com.example.demo.repository.oracle", entityManagerFactoryRef = "oracleEntityManagerFactory", transactionManagerRef = "oracleTransactionManager") public class OracleDataSourceConfiguration { @Bean(name = "oracleDataSourceProperties") @ConfigurationProperties("spring.second.datasource") public DataSourceProperties dataSourceProperties() { return new DataSourceProperties(); } @Bean @ConfigurationProperties("spring.second.datasource.configuration") public DataSource oracleDataSource(@Qualifier("oracleDataSourceProperties") DataSourceProperties oracleDataSourceProperties) { return oracleDataSourceProperties.initializeDataSourceBuilder() .type(HikariDataSource.class) .build(); } @Bean(name = "oracleEntityManagerFactory") public LocalContainerEntityManagerFactoryBean oracleEntityManagerFactory( EntityManagerFactoryBuilder builder, @Qualifier("oracleDataSource") DataSource oracleDataSource) { return builder.dataSource(oracleDataSource) .packages("com.example.demo.model.oracle") .persistenceUnit("oracle") .build(); } @Bean public PlatformTransactionManager oracleTransactionManager( final @Qualifier("oracleEntityManagerFactory") LocalContainerEntityManagerFactoryBean oracleEntityManagerFactory) { return new JpaTransactionManager(oracleEntityManagerFactory.getObject()); } }
總結
當僅有一個數據源時,Spring Boot會預設自動配置好,但是如果使用多個數據源時,需要進行一些自定義的配置,以上便是全部的配置。 總體感覺並不是特別複雜,耐心理解下,還是很容易理解的。 如有錯漏之處,還望各位大佬們指正。作者:吳家二少 部落格地址:https://www.cnblogs.com/cloudman-open/ 本文歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線