一、前言
作為一個資深的CRUD工程師,我們在實際使用springboot開發專案的時候,難免會遇到同時使用多個資料庫的情況,比如前腳剛查詢mysql,後腳就要查詢sqlserver。
這時,我們很直觀的就會想到,為springboot配置多個資料來源,需要用哪個資料庫連線,直接@Autowired不就行了。那麼問題來了,怎麼配置呢?
********************************************************************************************************************************************************************
退後,我要開始裝逼了
********************************************************************************************************************************************************************
二、前期工作
1.資料庫。
這裡我準備了一個mysql資料庫和一個sqlserver資料庫。
Mysql指令碼:
DROP TABLE IF EXISTS `tb_user`; CREATE TABLE `tb_user` ( `id` int(8) NOT NULL, `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of tb_user -- ---------------------------- INSERT INTO `tb_user` VALUES (0, '張三'); INSERT INTO `tb_user` VALUES (1, '李四'); INSERT INTO `tb_user` VALUES (2, '王五'); INSERT INTO `tb_user` VALUES (3, '趙六'); SET FOREIGN_KEY_CHECKS = 1;
sqlserver指令碼:
IF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[test]') AND type IN ('U')) DROP TABLE [dbo].[test] GO CREATE TABLE [dbo].[test] ( [id] int NOT NULL, [career] varchar(255) COLLATE Chinese_PRC_90_CI_AS NULL ) GO ALTER TABLE [dbo].[test] SET (LOCK_ESCALATION = TABLE) GO -- ---------------------------- -- Records of test -- ---------------------------- INSERT INTO [dbo].[test] ([id], [career]) VALUES (N'1', N'軟體工程師') GO INSERT INTO [dbo].[test] ([id], [career]) VALUES (N'2', N'硬體工程師') GO INSERT INTO [dbo].[test] ([id], [career]) VALUES (N'3', N'理財顧問') GO INSERT INTO [dbo].[test] ([id], [career]) VALUES (N'4', N'律師') GO INSERT INTO [dbo].[test] ([id], [career]) VALUES (N'5', N'數學家') GO -- ---------------------------- -- Primary Key structure for table test -- ---------------------------- ALTER TABLE [dbo].[test] ADD CONSTRAINT [PK__test__3213E83F1910436D] PRIMARY KEY CLUSTERED ([id]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] GO
2.專案依賴。
springboot版本:2.0.6.RELEASE
依賴這裡就不貼出來了,都是常見的幾個starter而已。
tk.mybatis外掛
這裡我著重說一下tk.mybatis外掛的配置。因為我們要使用該外掛自動生成mapper等相關檔案,但是我們又使用了兩個不同的資料庫,因此,需要對該外掛分別作不同的引數配置,然後分別自動生成。
注意不要同時配置外掛,不然會外掛引入衝突。
關於如何使用tk.mybatis外掛,請移步 使用mybatis-generator外掛結合tk.mybatis自動生成mapper二三事
<!-- mysql 資料庫 --> <!--<plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.5</version> <configuration> <configurationFile>src/main/resources/generator/generatorConfig-mysql.xml</configurationFile> <overwrite>true</overwrite> <verbose>true</verbose> </configuration> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper</artifactId> <version>3.4.4</version> </dependency> </dependencies> </plugin>--> <!-- sqlserver 資料庫 --> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.5</version> <configuration> <configurationFile>src/main/resources/generator/generatorConfig-sqlserver.xml</configurationFile> <overwrite>true</overwrite> <verbose>true</verbose> </configuration> <dependencies> <dependency> <groupId>com.microsoft.sqlserver</groupId> <artifactId>sqljdbc4</artifactId> <version>${sqlserver.sqljdbc4.version}</version> </dependency> <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper</artifactId> <version>3.4.4</version> </dependency> </dependencies> </plugin>
三、重頭戲
1.spring配置檔案
我們知道在application.yml(properties)檔案中,可以配置一個資料來源,spring在啟動時,會自動載入該配置,並例項化資料庫連線:
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://192.168.139.129:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
hikari:
minimum-idle: 1
maximum-pool-size: 20
但是我們現在有多個資料來源怎麼辦呢?難道直接粗暴的寫兩個?spring能自動識別兩個資料來源配置麼?
這時,我們就需要手動使用@Bean的方式在程式碼中進行不同資料來源的例項化配置了。為了更方便的管理配置資訊,所以,我們仍然將配置資訊寫在application.yml中便於屬性自動注入,但同時,對每一組資料來源配置資訊,需要加上字首用以區分。
spring: datasource: test-mysql: type: com.zaxxer.hikari.HikariDataSource url: jdbc:mysql://192.168.139.129:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: root driver-class-name: com.mysql.jdbc.Driver hikari: minimum-idle: 1 maximum-pool-size: 20 test-sqlserver: type: com.zaxxer.hikari.HikariDataSource url: jdbc:sqlserver://192.168.139.129;DatabaseName=dbo username: sa password: qwe!@#123 driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver hikari: minimum-idle: 1 maximum-pool-size: 20
2.手動配置資料來源
通過手動配置資料來源,分別例項化不同的資料來源連線物件,以實現spring的多資料來源配置。由於多個資料來源對於spring來說都是DataSource及其相關型別的Bean,那麼在spring容器進行DataSource例項化注入容器的時候,就會很困惑:WDNMD,你給勞資搞了幾個資料來源啊?這麼多“妹子”,我先“嘿咻”誰?所以,為了讓spring能夠順利的例項化我們配置的所有DataSource,就需要我們手動指定優先順序,使用@Primary註解告知spring當前Bean的優先順序更高。
主資料來源(mysql)
package com.zhangyu.springboot.multidatasource.config; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import tk.mybatis.spring.annotation.MapperScan; import javax.sql.DataSource; // mysql資料來源配置(主資料來源) @Configuration @MapperScan(basePackages = "com.zhangyu.springboot.multidatasource.mapper.mysql", sqlSessionTemplateRef = "mysqlSqlSessionTemplate") public class TestMysqlDataSourceConfig { // 注入資料來源配置 @Primary @Bean @ConfigurationProperties(prefix = "spring.datasource.test-mysql") public DataSourceProperties mysqlDataSourceProperties() { return new DataSourceProperties(); } // 建立資料庫連線 @Primary @Bean public DataSource mysqlDataSource() { return mysqlDataSourceProperties().initializeDataSourceBuilder().build(); } // 建立session工廠 @Primary @Bean public SqlSessionFactory mysqlSessionFactory(@Qualifier("mysqlDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource); sessionFactoryBean.setMapperLocations( new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml")); return sessionFactoryBean.getObject(); } // 建立事務管理(按需新增) @Primary @Bean public DataSourceTransactionManager mysqlDataSourceTransactionManager() { return new DataSourceTransactionManager(mysqlDataSource()); } // 會話管理 @Primary @Bean public SqlSessionTemplate mysqlSqlSessionTemplate(@Qualifier("mysqlSessionFactory") SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } }
次資料來源(sqlserver)
package com.zhangyu.springboot.multidatasource.config; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; 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.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import tk.mybatis.spring.annotation.MapperScan; import javax.sql.DataSource; // sqlserver資料來源配置 @Configuration @MapperScan(basePackages = "com.zhangyu.springboot.multidatasource.mapper.sqlserver", sqlSessionTemplateRef = "sqlServerSqlSessionTemplate") public class TestSqlServerDataSourceConfig { // 注入資料來源配置 @Bean @ConfigurationProperties(prefix = "spring.datasource.test-sqlserver") public DataSourceProperties sqlServerDataSourceProperties() { return new DataSourceProperties(); } // 建立資料庫連線 @Bean public DataSource sqlServerDataSource() { return sqlServerDataSourceProperties().initializeDataSourceBuilder().build(); } // 建立session工廠 @Bean public SqlSessionFactory sqlServerSessionFactory(@Qualifier("sqlServerDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource); sessionFactoryBean.setMapperLocations( new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml")); return sessionFactoryBean.getObject(); } // 建立事務管理(按需新增) @Bean public DataSourceTransactionManager sqlServerDataSourceTransactionManager() { return new DataSourceTransactionManager(sqlServerDataSource()); } // 會話管理 @Bean public SqlSessionTemplate sqlServerSqlSessionTemplate(@Qualifier("sqlServerSessionFactory") SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } }
再囉嗦幾句,mybatis是通過配置的掃描包和對應的sqlSessionTemplate來自動切換資料來源,即通過在 @MapperScan 註解中配置 basePackages 和 sqlSessionTemplateRef。
四、尾聲
啟動專案,可以看到控制檯列印如下資訊:
INFO 12840 --- [)-26.12.195.117] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... INFO 12840 --- [)-26.12.195.117] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. INFO 12840 --- [)-26.12.195.117] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Starting... INFO 12840 --- [)-26.12.195.117] com.zaxxer.hikari.pool.PoolBase : HikariPool-2 - Driver does not support get/set network timeout for connections. (null) INFO 12840 --- [)-26.12.195.117] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Start completed.
很明顯例項化了兩個 HikariPool ,實際使用又如何呢?
Usermapper連線的mysql資料庫,TestMapper連線的sqlserver資料庫:
//Service:
public List<TbUser> getUserList() { return userMapper.selectAll(); } public List<Test> getTestList() { return testMapper.selectAll(); }
分別呼叫,能都正常獲取資料。
至此,springboot雙資料來源配置完成,tk-mybatis也能正常使用。
********************************************************************************************************************************************************************
打完收工
********************************************************************************************************************************************************************