SpringBoot 的多數(shù)據(jù)源配置

2020-12-29 杭州 雪
最近在項目開發(fā)中,需要為一個使用 MySQL 數(shù)據(jù)庫的 SpringBoot 項目,新添加一個 PLSQL 數(shù)據(jù)庫數(shù)據(jù)源,那么就需要進行 SpringBoot 的多數(shù)據(jù)源開發(fā)。代碼很簡單,下面是實現(xiàn)的過程。
環(huán)境準備
實驗環(huán)境:
JDK 1.8 SpringBoot 2.4.1 Maven 3.6.3 MySQL 5.7
因為我本地只有 MySQL 數(shù)據(jù)庫,為了方便演示,我會在啟動一個本地 MySQL,在 MySQL 創(chuàng)建兩個數(shù)據(jù)庫,每個庫中均有一個表,以此進行演示。
數(shù)據(jù)準備
本地 MySQL 端口默認不做改動,端口號 3306。
創(chuàng)建數(shù)據(jù)庫 demo1,demo2。在 demo1 數(shù)據(jù)庫中創(chuàng)建表 book。
--?create?table
create?table?Book
(
????id??????????int?auto_increment
????????primary?key,
????author??????varchar(64)??not?null?comment?'作者信息',
????name????????varchar(64)??not?null?comment?'書籍名稱',
????price???????decimal??????not?null?comment?'價格',
????createTime??datetime?????null?comment?'上架時間',
????description?varchar(128)?null?comment?'書籍描述'
);
--?insert?data
INSERT?INTO?demo1.Book?(id,?author,?name,?price,?createTime,?description)?VALUES?(1,?'金庸',?'笑傲江湖',?13,?'2020-12-19?15:26:51',?'武俠小說');
INSERT?INTO?demo1.Book?(id,?author,?name,?price,?createTime,?description)?VALUES?(2,?'羅貫中',?'三國演義',?14,?'2020-12-19?15:28:36',?'歷史小說');
在 demo2 數(shù)據(jù)庫中創(chuàng)建表 user。
--?create?table
create?table?User
(
????id???????int?auto_increment
????????primary?key,
????name?????varchar(32)?null?comment?'用戶名稱',
????birthday?date????????null?comment?'出生日期'
)
????comment?'用戶信息表';
--?insert?data
INSERT?INTO?demo2.User?(id,?name,?birthday)?VALUES?(1,?'金庸',?'1924-03-10');
INSERT?INTO?demo2.User?(id,?name,?birthday)?VALUES?(2,?'羅貫中',?'1330-01-10');
數(shù)據(jù)準備完畢,表中都新增了兩條數(shù)據(jù)。
項目準備
這里直接從 Spring 官方上初始化一個添加了 web、lombok、mybatis、mysql 依賴的 SpringBoot 項目。
訪問直接下載:
https://start.spring.io/starter.zip?type=maven-project&language=java&bootVersion=2.4.1.RELEASE&baseDir=demo&groupId=com&artifactId=wdbyte&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com.wdbyte.demo&packaging=jar&javaVersion=1.8&dependencies=mybatis,lombok,web,mysql
如果你手上已經(jīng)有了一個 SpringBoot 項目,既然你想改造成多數(shù)據(jù)源,那么你應該已經(jīng)有了一個數(shù)據(jù)源了,如果新增的數(shù)據(jù)源數(shù)據(jù)庫和目前的一致,你可以直接使用你的項目進行改造測試。
多數(shù)據(jù)源
SpringBoot 的多數(shù)據(jù)源開發(fā)十分簡單,如果多個數(shù)據(jù)源的數(shù)據(jù)庫相同,比如都是 MySQL,那么依賴是不需要任何改動的,只需要進行多數(shù)據(jù)源配置即可。
如果你新增的數(shù)據(jù)庫數(shù)據(jù)源和目前的數(shù)據(jù)庫不同,記得引入新數(shù)據(jù)庫的驅(qū)動依賴,比如 MySQL 和 PGSQL。
<dependency>
?<groupId>mysqlgroupId>
?<artifactId>mysql-connector-javaartifactId>
?<scope>runtimescope>
dependency>
<dependency>
????<groupId>org.postgresqlgroupId>
????<artifactId>postgresqlartifactId>
????<version>42.2.7version>
dependency>
連接配置
既然有多個數(shù)據(jù)源,因為數(shù)據(jù)庫用戶名密碼可能不相同,所以是需要配置多個數(shù)據(jù)源信息的,直接在 properties/yml 中配置即可。這里要注意根據(jù)配置的屬性名進行區(qū)分,同時因為數(shù)據(jù)源要有一個默認使用的數(shù)據(jù)源,最好在名稱上有所區(qū)分(這里使用 primary 作為主數(shù)據(jù)源標識)。
########################## 主數(shù)據(jù)源 ##################################
spring.datasource.primary.jdbc-url=jdbc:mysql://127.0.0.1:3306/demo1?characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.primary.username=root
spring.datasource.primary.password=
########################## 第二個數(shù)據(jù)源 ###############################
spring.datasource.datasource2.jdbc-url=jdbc:mysql://127.0.0.1:3306/demo2?characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.datasource2.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.datasource2.username=root
spring.datasource.datasource2.password=
# mybatis
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.wdbyte.domain
注意,配置中的數(shù)據(jù)源連接 url 末尾使用的是 jdbc-url.
因為使用了 Mybatis 框架,所以 Mybatis 框架的配置信息也是少不了的,指定掃描目錄 mapper 下的mapper xml 配置文件。
Mybatis 配置
如何編寫 Mybatis Mapper 或者如何使用工具生成 Mybatis Mapper 不是本文的重點,如果你不知道可以參考 Mybatis 官方文檔或者我之前的文章。
鏈接一:使用 Mybatis(自動生成插件) 訪問數(shù)據(jù)庫
鏈接二:使用 Mybatis 集成 pagehelper 分頁插件和 mapper 插件
下面我已經(jīng)按照上面的兩個庫中的兩個表,Book 和 User 表分別編寫相應的 Mybatis 配置。
創(chuàng)建 BookMapper.xml 和 UserMapper.xml 放到配置文件配置的路徑 mapper 目錄下。創(chuàng)建 UserMapper 和 BookMapper 接口操作類放在不同的目錄。這里注意 Mapper 接口要按數(shù)據(jù)源分開放在不同的目錄中。后續(xù)好使用不同的數(shù)據(jù)源配置掃描不同的目錄,這樣就可以實現(xiàn)不同的 Mapper 使用不同的數(shù)據(jù)源配置。

Service 層沒有變化,這里 BookMapper 和 UserMapper 都有一個 selectAll() 方法用于查詢測試。
多數(shù)據(jù)源配置
上面你應該看到了,到目前為止和 Mybatis 單數(shù)據(jù)源寫法唯一的區(qū)別就是 Mapper 接口使用不同的目錄分開了,那么這個不同點一定會在數(shù)據(jù)源配置中體現(xiàn)。
主數(shù)據(jù)源
開始配置兩個數(shù)據(jù)源信息,先配置主數(shù)據(jù)源,配置掃描的 MapperScan 目錄為 com.wdbyte.mapper.primary
/**
?*?主數(shù)據(jù)源配置
?*
?*?@author?niujinpeng
?*?@website:?https://www.wdbyte.com
?*?@date?2020/12/19
?*/
@Configuration
@MapperScan(basePackages?=?{"com.wdbyte.mapper.primary"},?sqlSessionFactoryRef?=?"sqlSessionFactory")
public?class?PrimaryDataSourceConfig?{
????@Bean(name?=?"dataSource")
????@ConfigurationProperties(prefix?=?"spring.datasource.primary")
????@Primary
????public?DataSource?dataSource()?{
????????return?DataSourceBuilder.create().build();
????}
????@Bean(name?=?"sqlSessionFactory")
????@Primary
????public?SqlSessionFactory?sqlSessionFactory(@Qualifier("dataSource")?DataSource?dataSource)?throws?Exception?{
????????SqlSessionFactoryBean?bean?=?new?SqlSessionFactoryBean();
????????bean.setDataSource(dataSource);
????????bean.setMapperLocations(new?PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
????????return?bean.getObject();
????}
????@Bean(name?=?"transactionManager")
????@Primary
????public?DataSourceTransactionManager?transactionManager(@Qualifier("dataSource")?DataSource?dataSource)?{
????????return?new?DataSourceTransactionManager(dataSource);
????}
????@Bean(name?=?"sqlSessionTemplate")
????@Primary
????public?SqlSessionTemplate?sqlSessionTemplate(@Qualifier("sqlSessionFactory")?SqlSessionFactory?sqlSessionFactory)?{
????????return?new?SqlSessionTemplate(sqlSessionFactory);
????}
}
和單數(shù)據(jù)源不同的是這里把
dataSourcesqlSessionFactorytransactionManagersqlSessionTemplate
都單獨進行了配置,簡單的 bean 創(chuàng)建,下面是用到的一些注解說明。
@ConfigurationProperties(prefix = "spring.datasource.primary"):使用spring.datasource.primary 開頭的配置。@Primary:聲明這是一個主數(shù)據(jù)源(默認數(shù)據(jù)源),多數(shù)據(jù)源配置時必不可少。@Qualifier:顯式選擇傳入的 Bean。
第二個數(shù)據(jù)源
第二個數(shù)據(jù)源和主數(shù)據(jù)源唯一不同的只是 MapperScan 掃描路徑和創(chuàng)建的 Bean 名稱,同時沒有 @Primary 主數(shù)據(jù)源的注解。
/**
?*?第二個數(shù)據(jù)源配置
?*?
?*?@author?niujinpeng
?*?@website:?https://www.wdbyte.com
?*?@date?2020/12/19
?*/
@Configuration
@MapperScan(basePackages?=?{"com.wdbyte.mapper.datasource2"},?sqlSessionFactoryRef?=?"sqlSessionFactory2")
public?class?SecondDataSourceConfig?{
????@Bean(name?=?"dataSource2")
????@ConfigurationProperties(prefix?=?"spring.datasource.datasource2")
????public?DataSource?dataSource()?{
????????return?DataSourceBuilder.create().build();
????}
????@Bean(name?=?"sqlSessionFactory2")
????public?SqlSessionFactory?sqlSessionFactory(@Qualifier("dataSource2")?DataSource?dataSource)?throws?Exception?{
????????SqlSessionFactoryBean?bean?=?new?SqlSessionFactoryBean();
????????bean.setDataSource(dataSource);
????????bean.setMapperLocations(new?PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
????????return?bean.getObject();
????}
????@Bean(name?=?"transactionManager2")
????public?DataSourceTransactionManager?transactionManager(@Qualifier("dataSource2")?DataSource?dataSource)?{
????????return?new?DataSourceTransactionManager(dataSource);
????}
????@Bean(name?=?"sqlSessionTemplate2")
????public?SqlSessionTemplate?sqlSessionTemplate(@Qualifier("sqlSessionFactory2")?SqlSessionFactory?sqlSessionFactory)?{
????????return?new?SqlSessionTemplate(sqlSessionFactory);
????}
}
注意:因為已經(jīng)在兩個數(shù)據(jù)源中分別配置了掃描的 Mapper 路徑,如果你之前在 SpringBoot 啟動類中也使用了 Mapper 掃描注解,需要刪掉。
訪問測試
編寫兩個簡單的查詢 Controller 然后進行訪問測試。
//?BookController
@RestController
public?class?BookController?{
????@Autowired
????private?BookService?bookService;
????@GetMapping(value?=?"/books")
????public?Response?selectAll()?throws?Exception?{
????????List?books?=?bookService.selectAll();
????????return?ResponseUtill.success(books);
????}
}
//?UserController
@RestController
public?class?UserController?{
????@Autowired
????private?UserService?userService;
????@ResponseBody
????@GetMapping(value?=?"/users")
????public?Response?selectAll()?{
????????List?userList?=?userService.selectAll();
????????return?ResponseUtill.success(userList);
????}
}
訪問測試,我這里直接 CURL 請求。
???~?curl?localhost:8080/books?
{
??"code":?"0000",
??"message":?"success",
??"data":?[
????{
??????"id":?1,
??????"author":?"金庸",
??????"name":?"笑傲江湖",
??????"price":?13,
??????"createtime":?"2020-12-19T07:26:51.000+00:00",
??????"description":?"武俠小說"
????},
????{
??????"id":?2,
??????"author":?"羅貫中",
??????"name":?"三國演義",
??????"price":?14,
??????"createtime":?"2020-12-19T07:28:36.000+00:00",
??????"description":?"歷史小說"
????}
??]
}
???~?curl?localhost:8080/users?
{
??"code":?"0000",
??"message":?"success",
??"data":?[
????{
??????"id":?1,
??????"name":?"金庸",
??????"birthday":?"1924-03-09T16:00:00.000+00:00"
????},
????{
??????"id":?2,
??????"name":?"羅貫中",
??????"birthday":?"1330-01-09T16:00:00.000+00:00"
????}
??]
}
???~
至此,多數(shù)據(jù)源配置完成,測試成功。
連接池
其實在多數(shù)據(jù)源改造中,我們一般情況下都不會使用默認的 JDBC 連接方式,往往都需要引入連接池進行連接優(yōu)化,不然你可能會經(jīng)常遇到數(shù)據(jù)源連接被斷開等報錯日志。其實數(shù)據(jù)源切換連接池數(shù)據(jù)源也是十分簡單的,直接引入連接池依賴,然后把創(chuàng)建 dataSource 的部分換成連接池數(shù)據(jù)源創(chuàng)建即可。
下面以阿里的 Druid 為例,先引入連接池數(shù)據(jù)源依賴。
<dependency>
???<groupId>com.alibabagroupId>
???<artifactId>druidartifactId>
dependency>
添加 Druid 的一些配置。
spring.datasource.datasource2.initialSize=3 # 根據(jù)自己情況設(shè)置
spring.datasource.datasource2.minIdle=3
spring.datasource.datasource2.maxActive=20
改寫 dataSource Bean 的創(chuàng)建代碼部分。
@Value("${spring.datasource.datasource2.jdbc-url}")
private?String?url;
@Value("${spring.datasource.datasource2.driver-class-name}")
private?String?driverClassName;
@Value("${spring.datasource.datasource2.username}")
private?String?username;
@Value("${spring.datasource.datasource2.password}")
private?String?password;
@Value("${spring.datasource.datasource2.initialSize}")
private?int?initialSize;
@Value("${spring.datasource.datasource2.minIdle}")
private?int?minIdle;
@Value("${spring.datasource.datasource2.maxActive}")
private?int?maxActive;
@Bean(name?=?"dataSource2")
public?DataSource?dataSource()?{
????DruidDataSource?dataSource?=?new?DruidDataSource();
????dataSource.setUrl(url);
????dataSource.setDriverClassName(driverClassName);
????dataSource.setUsername(username);
????dataSource.setPassword(password);
????dataSource.setInitialSize(initialSize);
????dataSource.setMinIdle(minIdle);
????dataSource.setMaxActive(maxActive);
????return?dataSource;
}
這里只是簡單的提一下使用連接池的重要性,Druid 的詳細用法還請參考官方文檔。
文中代碼已經(jīng)上傳到 Github:?
https://github.com/niumoo/springboot
最后的話
文章有幫助可以點個「在看」或「分享」,都是支持!
文章每周持續(xù)更新,可以關(guān)注「 未讀代碼 」公眾號。
---- END ----
"未讀代碼,一線技術(shù)工具人的學習、生活與見聞"
點個在看,給我加油充電~?
