一、前言
作為一個資深的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也能正常使用。
********************************************************************************************************************************************************************
打完收工
********************************************************************************************************************************************************************
