更新時間:2022-11-21 來源:黑馬程序員 瀏覽量:
1.背景
通常一個系統(tǒng)只需要連接一個數(shù)據(jù)庫就可以了。但是在企業(yè)應(yīng)用的開發(fā)中往往會和其他子系統(tǒng)交互,特別是對于一些數(shù)據(jù)實時性要求比較高的數(shù)據(jù),我們就需要做實時連接查詢,而不是做同步。這個時候就需要用到多數(shù)據(jù)源。
舉個簡單的例子某企業(yè)要做訂單網(wǎng)上訂單系統(tǒng)這里面就可以涉及到多個子系統(tǒng)的連接,比如:產(chǎn)品主數(shù)據(jù)的數(shù)據(jù)源,項目管理系統(tǒng)的數(shù)據(jù)源(項目可以產(chǎn)品訂單)等多個不同數(shù)據(jù)庫類似的數(shù)據(jù)源,他們可能是ORACLE,SQL SERVER,MYSQL等多種混合數(shù)據(jù)源。
2.多數(shù)據(jù)源概述
基于以上的背景,就會選擇使用多個數(shù)據(jù)源,一個數(shù)據(jù)源用于讀一個數(shù)據(jù)源用于寫。或者不同的數(shù)據(jù)源混合使用。他的基本思想其實就是AOP。我們可以通過AOP的思想實現(xiàn)動態(tài)數(shù)據(jù)源切換,通過這個AOP思想可適用于多種場景、純粹多庫、讀寫分離、一主多從、混合模式等。
動態(tài)數(shù)據(jù)源能進(jìn)行自動切換的核心就是spring底層的AbstractRoutingDataSource進(jìn)行數(shù)據(jù)源的路由,只要繼承了這個類均可看作是一個數(shù)據(jù)源的實現(xiàn)。主要實現(xiàn)方法是determineCurrentLookupkey,該方法只需要返回數(shù)據(jù)源實例名稱。
3.mybatisplus多數(shù)據(jù)源
我們在項目中用mybatisplus的使用用得比較多,這個動態(tài)數(shù)據(jù)源切換需要實現(xiàn)的話,比較麻煩,如果有現(xiàn)成的框架使用則最好不過了。恰好mybatiplus就能實現(xiàn)。文檔地址如下:
```properties
https://baomidou.com/pages/a61e1b/#%E6%96%87%E6%A1%A3-documentation
https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611
```
4.使用
4.1介紹
dynamic-datasource-spring-boot-starter是一個基于springboot的快速集成多數(shù)據(jù)源的啟動器。
特性:
-支持**數(shù)據(jù)源分組**,適用于多種場景純粹多庫讀寫分離一主多從混合模式。
-支持?jǐn)?shù)據(jù)庫敏感配置信息**加密**ENC。
-支持每個數(shù)據(jù)庫獨立初始化表結(jié)構(gòu)schema和數(shù)據(jù)庫database。
-支持無數(shù)據(jù)源啟動,支持懶加載數(shù)據(jù)源(需要的時候再創(chuàng)建連接)。
-支持**自定義注解**,需繼承DS3.2.0+。
-提供并簡化對Druid,HikariCp,BeeCp,Dbcp2的快速集成。
-提供對Mybatis-Plus,Quartz,ShardingJdbc,P6sy,Jndi等組件的集成方案。
-提供**自定義數(shù)據(jù)源來源**方案(如全從數(shù)據(jù)庫加載)。
-提供項目啟動后**動態(tài)增加移除數(shù)據(jù)源**方案。
-提供Mybatis環(huán)境下的**純讀寫分離**方案。
-提供使用**spel動態(tài)參數(shù)**解析數(shù)據(jù)源方案。內(nèi)置spel,session,header,支持自定義。
-支持**多層數(shù)據(jù)源嵌套切換**。(ServiceA>>>ServiceB>>>ServiceC)。
-提供**基于seata的分布式事務(wù)方案。
-提供**本地多數(shù)據(jù)源事務(wù)方案。**
4.2 約定
1.本框架只做**切換數(shù)據(jù)源**這件核心的事情,并**不限制你的具體操作**,切換了數(shù)據(jù)源可以做任何CRUD。
2.配置文件所有以下劃線`_`分割的數(shù)據(jù)源**首部**即為組的名稱,相同組名稱的數(shù)據(jù)源會放在一個組下。
3.切換數(shù)據(jù)源可以是組名,也可以是具體數(shù)據(jù)源名稱。組名則切換時采用負(fù)載均衡算法切換。
4.默認(rèn)的數(shù)據(jù)源名稱為**master**,你可以通過`spring.datasource.dynamic.primary`修改。
5.方法上的注解優(yōu)先于類上注解。
6.DS支持繼承抽象類上的DS,暫不支持繼承接口上的DS。
4.3 使用
4.3.1準(zhǔn)備數(shù)據(jù)庫
docker run --name mysq -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 -d mysql:5.7
創(chuàng)建2個數(shù)據(jù)庫
create database test1; create database test2; use test2; -- auto-generated definition create table tb_user ( id int auto_increment primary key, name varchar(200) null ); insert into tb_user values(1,"wangwu"); use test1; -- auto-generated definition create table tb_user ( id int auto_increment primary key, name varchar(200) null ); insert into tb_user values(1,"zhangsan");
一個作為主庫一個作為從庫。
4.3.2 springboot創(chuàng)建工程
添加依賴:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.itheima</groupId> <artifactId>dynamic</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.9.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.21</version> </dependency> </dependencies> </project>
啟動類:
package com.itheima; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DynamicApplication { public static void main(String[] args) { SpringApplication.run(DynamicApplication.class,args); } }
4.3.3配置yml
spring: datasource: dynamic: primary: master #設(shè)置默認(rèn)的數(shù)據(jù)源或者數(shù)據(jù)源組,默認(rèn)值即為master strict: false #嚴(yán)格匹配數(shù)據(jù)源,默認(rèn)false. true未匹配到指定數(shù)據(jù)源時拋異常,false使用默認(rèn)數(shù)據(jù)源 datasource: master: url: jdbc:mysql://192.168.211.253:3306/test1 username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver # 3.2.0開始支持SPI可省略此配置 slave_1: url: jdbc:mysql://192.168.211.253:3306/test2 username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver #......省略 #以上會配置一個默認(rèn)庫master,一個組slave下有兩個子庫slave_1
4.3.4使用注解來切換數(shù)據(jù)源
使用**DS**切換數(shù)據(jù)源,使用方式如下:
**DS**可以注解在方法上或類上,**同時存在就近原則方法上注解優(yōu)先于類上注解**。
|注解|結(jié)果|
|-------------|----------------------------------------|
|沒有DS|默認(rèn)數(shù)據(jù)源|
|DS
"dsName"
|dsName可以為組名也可以為具體某個庫的名稱|
例如:
@Service @DS("slave") public class UserServiceImpl implements UserService { @Autowired private JdbcTemplate jdbcTemplate; public List selectAll() { return jdbcTemplate.queryForList("select * from user"); } @Override @DS("slave_1") public List selectByCondition() { return jdbcTemplate.queryForList("select * from user where age >10"); } }
4.3.5創(chuàng)建CSD
po:
package com.itheima.po; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @Data @TableName("tb_user") public class User { @TableId(type = IdType.AUTO) private Integer id; @TableField("name") private String name; }
controller
package com.itheima.controller; import com.itheima.po.User; import com.itheima.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @GetMapping("/{id}") public User get(@PathVariable(name="id")Integer id){ return userService.getById(id); } }
service
```
public interface UserService{
User getById
Integer id
;
}
```
package com.itheima.service.impl; import com.baomidou.dynamic.datasource.annotation.DS; import com.itheima.dao.UserDao; import com.itheima.po.User; import com.itheima.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service @DS("slave_1") public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override public User getById(Integer id) { return userDao.selectById(id); } }
注意:如上:DS注解用于指定使用哪一個數(shù)據(jù)源。
dao
package com.itheima.dao; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.itheima.po.User; public interface UserDao extends BaseMapper<User> { }
啟動類:
@SpringBootApplication @MapperScan(basePackages = "com.itheima.dao") public class DynamicApplication { public static void main(String[] args) { SpringApplication.run(DynamicApplication.class,args); } }
4.3.6測試
+當(dāng)使用DS注解指定master的時候:
```
Service
DS
"master"
public class UserServiceImpl implements UserService{
Autowired
private UserDao userDao;
Override
public User getById
Integer id
{
return userDao.selectById
id
;
}
}
```
重啟項目,瀏覽器發(fā)送請求:http://localhost:8080/user/1
得到結(jié)果:
```
{"id":1,"name":"zhangsan"}
```
+當(dāng)使用DS注解指定slave_1的時候
```
Service
DS
"slave_1"
public class UserServiceImpl implements UserService{
Autowired
private UserDao userDao;
Override
public User getById
Integer id
{
return userDao.selectById
id
;
}
}
```
重啟項目,瀏覽器發(fā)送請求:http://localhost:8080/user/1
得到結(jié)果:
{"id":1,"name":"wangwu"}
測試成功。
5.總結(jié)
使用mybatisplus的動態(tài)數(shù)據(jù)源切換非常方便,只需添加依賴,并在yaml中配置數(shù)據(jù)源的名稱和地址,并在service的實現(xiàn)類中使用注解來指定實現(xiàn)切換即可。下一章節(jié)我們來看看如何使用AOP來實現(xiàn)不需要修改代碼就能動態(tài)切換數(shù)據(jù)源。
+添加依賴
+添加yaml配置
+在業(yè)務(wù)方法上或者業(yè)務(wù)類上添加 DS注解