Spring boot的Mybatis多數(shù)據(jù)源配置
最近在項(xiàng)目開(kāi)發(fā)中,需要為一個(gè)使用 MySQL 數(shù)據(jù)庫(kù)的 SpringBoot 項(xiàng)目,新添加一個(gè) PLSQL 數(shù)據(jù)庫(kù)數(shù)據(jù)源,那么就需要進(jìn)行 SpringBoot 的多數(shù)據(jù)源開(kāi)發(fā)。代碼很簡(jiǎn)單,下面是實(shí)現(xiàn)的過(guò)程。
環(huán)境準(zhǔn)備
實(shí)驗(yàn)環(huán)境:
- JDK 1.8
- SpringBoot 2.4.1
- Maven 3.6.3
- MySQL 5.7
因?yàn)槲冶镜刂挥?MySQL 數(shù)據(jù)庫(kù),為了方便演示,我會(huì)在啟動(dòng)一個(gè)本地 MySQL,在 MySQL 創(chuàng)建兩個(gè)數(shù)據(jù)庫(kù),每個(gè)庫(kù)中均有一個(gè)表,以此進(jìn)行演示。
數(shù)據(jù)準(zhǔn)備
本地 MySQL 端口默認(rèn)不做改動(dòng),端口號(hào) 3306。
創(chuàng)建數(shù)據(jù)庫(kù) demo1,demo2。在 demo1 數(shù)據(jù)庫(kù)中創(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 '書(shū)籍名稱(chēng)',
- price decimal not null comment '價(jià)格',
- createTime datetime null comment '上架時(shí)間',
- description varchar(128) null comment '書(shū)籍描述'
- );
- -- insert data
- INSERT INTO demo1.Book (id, author, name, price, createTime, description) VALUES (1, '金庸', '笑傲江湖', 13, '2020-12-19 15:26:51', '武俠小說(shuō)');
- INSERT INTO demo1.Book (id, author, name, price, createTime, description) VALUES (2, '羅貫中', '三國(guó)演義', 14, '2020-12-19 15:28:36', '歷史小說(shuō)');
在 demo2 數(shù)據(jù)庫(kù)中創(chuàng)建表 user。
- -- create table
- create table User
- (
- id int auto_increment
- primary key,
- name varchar(32) null comment '用戶(hù)名稱(chēng)',
- birthday date null comment '出生日期'
- )
- comment '用戶(hù)信息表';
- -- 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ù)準(zhǔn)備完畢,表中都新增了兩條數(shù)據(jù)。
項(xiàng)目準(zhǔn)備
這里直接從 Spring 官方上初始化一個(gè)添加了 web、lombok、mybatis、mysql 依賴(lài)的 SpringBoot 項(xiàng)目。
- 訪(fǎng)問(wèn)直接下載: 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)有了一個(gè) SpringBoot 項(xiàng)目,既然你想改造成多數(shù)據(jù)源,那么你應(yīng)該已經(jīng)有了一個(gè)數(shù)據(jù)源了,如果新增的數(shù)據(jù)源數(shù)據(jù)庫(kù)和目前的一致,你可以直接使用你的項(xiàng)目進(jìn)行改造測(cè)試。
多數(shù)據(jù)源
SpringBoot 的多數(shù)據(jù)源開(kāi)發(fā)十分簡(jiǎn)單,如果多個(gè)數(shù)據(jù)源的數(shù)據(jù)庫(kù)相同,比如都是 MySQL,那么依賴(lài)是不需要任何改動(dòng)的,只需要進(jìn)行多數(shù)據(jù)源配置即可。
如果你新增的數(shù)據(jù)庫(kù)數(shù)據(jù)源和目前的數(shù)據(jù)庫(kù)不同,記得引入新數(shù)據(jù)庫(kù)的驅(qū)動(dòng)依賴(lài),比如 MySQL 和 PGSQL。
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <scope>runtime</scope>
- </dependency>
- <dependency>
- <groupId>org.postgresql</groupId>
- <artifactId>postgresql</artifactId>
- <version>42.2.7</version>
- </dependency>
連接配置
既然有多個(gè)數(shù)據(jù)源,因?yàn)閿?shù)據(jù)庫(kù)用戶(hù)名密碼可能不相同,所以是需要配置多個(gè)數(shù)據(jù)源信息的,直接在 properties/yml 中配置即可。這里要注意根據(jù)配置的屬性名進(jìn)行區(qū)分,同時(shí)因?yàn)閿?shù)據(jù)源要有一個(gè)默認(rèn)使用的數(shù)據(jù)源,最好在名稱(chēng)上有所區(qū)分(這里使用 primary 作為主數(shù)據(jù)源標(biāo)識(shí))。
- ########################## 主數(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=
- ########################## 第二個(gè)數(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 .
因?yàn)槭褂昧?Mybatis 框架,所以 Mybatis 框架的配置信息也是少不了的,指定掃描目錄mapper 下的 mapper xml 配置文件。
Mybatis 配置
如何編寫(xiě) Mybatis Mapper 或者如何使用工具生成 Mybatis Mapper 不是本文的重點(diǎn),如果你不知道可以參考 Mybatis 官方文檔或者我之前的文章。
鏈接一: 使用 Mybatis(自動(dòng)生成插件) 訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)
鏈接二: 使用 Mybatis 集成 pagehelper 分頁(yè)插件和 mapper 插件
下面我已經(jīng)按照上面的兩個(gè)庫(kù)中的兩個(gè)表,Book 和 User 表分別編寫(xiě)相應(yīng)的 Mybatis 配置。
創(chuàng)建 BookMapper.xml 和 UserMapper.xml 放到配置文件配置的路徑 mapper 目錄下。創(chuàng)建 UserMapper 和 BookMapper 接口操作類(lèi)放在不同的目錄。這里注意 Mapper 接口要按數(shù)據(jù)源分開(kāi)放在不同的目錄中。后續(xù)好使用不同的數(shù)據(jù)源配置掃描不同的目錄,這樣就可以實(shí)現(xiàn)不同的 Mapper 使用不同的數(shù)據(jù)源配置。

Service 層沒(méi)有變化,這里 BookMapper 和 UserMapper 都有一個(gè) selectAll() 方法用于查詢(xún)測(cè)試。
多數(shù)據(jù)源配置
上面你應(yīng)該看到了,到目前為止和 Mybatis 單數(shù)據(jù)源寫(xiě)法唯一的區(qū)別就是 Mapper 接口使用不同的目錄分開(kāi)了,那么這個(gè)不同點(diǎn)一定會(huì)在數(shù)據(jù)源配置中體現(xiàn)。
主數(shù)據(jù)源
開(kāi)始配置兩個(gè)數(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ù)源不同的是這里把
- dataSource
- sqlSessionFactory
- transactionManager
- sqlSessionTemplate
都單獨(dú)進(jìn)行了配置,簡(jiǎn)單的 bean 創(chuàng)建,下面是用到的一些注解說(shuō)明。
- @ConfigurationProperties(prefix = "spring.datasource.primary") :使用spring.datasource.primary 開(kāi)頭的配置。
- @Primary :聲明這是一個(gè)主數(shù)據(jù)源(默認(rèn)數(shù)據(jù)源),多數(shù)據(jù)源配置時(shí) 必不可少 。
- @Qualifier :顯式選擇傳入的 Bean。
第二個(gè)數(shù)據(jù)源
第二個(gè)數(shù)據(jù)源和主數(shù)據(jù)源唯一不同的只是 MapperScan 掃描路徑和創(chuàng)建的 Bean 名稱(chēng),同時(shí)沒(méi)有 @Primary 主數(shù)據(jù)源的注解。
- /**
- * 第二個(gè)數(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);
- }
- }
注意:因?yàn)橐呀?jīng)在兩個(gè)數(shù)據(jù)源中分別配置了掃描的 Mapper 路徑,如果你之前在 SpringBoot 啟動(dòng)類(lèi)中也使用了 Mapper 掃描注解, 需要?jiǎng)h掉 。
訪(fǎng)問(wèn)測(cè)試
編寫(xiě)兩個(gè)簡(jiǎn)單的查詢(xún) Controller 然后進(jìn)行訪(fǎng)問(wèn)測(cè)試。
- // BookController
- @RestController
- public class BookController {
- @Autowired
- private BookService bookService;
- @GetMapping(value = "/books")
- public Response selectAll() throws Exception {
- List<Book> books = bookService.selectAll();
- return ResponseUtill.success(books);
- }
- }
- // UserController
- @RestController
- public class UserController {
- @Autowired
- private UserService userService;
- @ResponseBody
- @GetMapping(value = "/users")
- public Response selectAll() {
- List<User> userList = userService.selectAll();
- return ResponseUtill.success(userList);
- }
- }
訪(fǎng)問(wèn)測(cè)試,我這里直接 CURL 請(qǐng)求 。
- ➜ ~ 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": "武俠小說(shuō)"
- },
- {
- "id": 2,
- "author": "羅貫中",
- "name": "三國(guó)演義",
- "price": 14,
- "createtime": "2020-12-19T07:28:36.000+00:00",
- "description": "歷史小說(shuō)"
- }
- ]
- }
- ➜ ~ 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ù)源配置完成,測(cè)試成功。
連接池
其實(shí)在多數(shù)據(jù)源改造中,我們一般情況下都不會(huì)使用默認(rèn)的 JDBC 連接方式,往往都需要引入連接池進(jìn)行連接優(yōu)化,不然你可能會(huì)經(jīng)常遇到數(shù)據(jù)源連接被斷開(kāi)等報(bào)錯(cuò)日志。其實(shí)數(shù)據(jù)源切換連接池?cái)?shù)據(jù)源也是十分簡(jiǎn)單的,直接引入連接池依賴(lài),然后把創(chuàng)建 dataSource 的部分換成連接池?cái)?shù)據(jù)源創(chuàng)建即可。
下面以阿里的 Druid 為例,先引入連接池?cái)?shù)據(jù)源依賴(lài)。
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>druid</artifactId>
- </dependency>
添加 Druid 的一些配置。
- spring.datasource.datasource2.initialSize=3 # 根據(jù)自己情況設(shè)置
- spring.datasource.datasource2.minIdle=3
- spring.datasource.datasource2.maxActive=20
改寫(xiě) 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;
- }
【編輯推薦】