詳解SpringBoot+Mybatis實現(xiàn)動態(tài)數(shù)據(jù)源切換
業(yè)務(wù)背景
電商訂單項目分正向和逆向兩個部分:其中正向數(shù)據(jù)庫記錄了訂單的基本信息,包括訂單基本信息、訂單商品信息、優(yōu)惠卷信息、發(fā)票信息、賬期信息、結(jié)算信息、訂單備注信息、收貨人信息等;逆向數(shù)據(jù)庫主要包含了商品的退貨信息和維修信息。數(shù)據(jù)量超過500萬行就要考慮分庫分表和讀寫分離,那么我們在正向操作和逆向操作的時候,就需要動態(tài)的切換到相應(yīng)的數(shù)據(jù)庫,進(jìn)行相關(guān)的操作。
解決思路
現(xiàn)在項目的結(jié)構(gòu)設(shè)計基本上是基于MVC的,那么數(shù)據(jù)庫的操作集中在dao層完成,主要業(yè)務(wù)邏輯在service層處理,controller層處理請求。假設(shè)在執(zhí)行dao層代碼之前能夠?qū)?shù)據(jù)源(DataSource)換成我們想要執(zhí)行操作的數(shù)據(jù)源,那么這個問題就解決了
環(huán)境準(zhǔn)備:
1.實體類
@Data
public class Product {
private Integer id;
private String name;
private Double price;
}
2.ProductMapper
public interface ProductMapper {
@Select("select * from product")
public List<Product> findAllProductM();
@Select("select * from product")
public List<Product> findAllProductS();
}
3.ProductService
@Service
public class ProductService {
@Autowired
private ProductMapper productMapper;
public void findAllProductM(){
// 查詢Master
List<Product> allProductM = productMapper.findAllProductM();
System.out.println(allProductM);
}
public void findAllProductS(){
// 查詢Slave
List<Product> allProductS = productMapper.findAllProductS();
System.out.println(allProductS);
}
}
具體實現(xiàn)
第一步:配置多數(shù)據(jù)源
首先,我們在application.properties中配置兩個數(shù)據(jù)源
spring.druid.datasource.master.password=root
spring.druid.datasource.master.username=root
spring.druid.datasource.master.jdbc- url=jdbc:mysql://localhost:3306/product_master? useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
spring.druid.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
spring.druid.datasource.slave.password=root
spring.druid.datasource.slave.username=root
spring.druid.datasource.slave.jdbc- url=jdbc:mysql://localhost:3306/product_slave? useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
spring.druid.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driver
在SpringBoot的配置代碼中,我們初始化兩個數(shù)據(jù)源:
@Configuration
public class MyDataSourceConfiguratioin {
Logger logger = LoggerFactory.getLogger(MyDataSourceConfiguratioin.class);
/*** Master data source. */
@Bean("masterDataSource")
@ConfigurationProperties(prefix = "spring.druid.datasource.master")
DataSource masterDataSource() {
logger.info("create master datasource...");
return DataSourceBuilder.create().build();
}
/*** Slave data source. */
@Bean("slaveDataSource")
@ConfigurationProperties(prefix = "spring.druid.datasource.slave")
DataSource slaveDataSource() {
logger.info("create slave datasource...");
return DataSourceBuilder.create().build();
}
@Bean
@Primary
DataSource primaryDataSource(@Autowired @Qualifier("masterDataSource")DataSource masterDataSource,
@Autowired @Qualifier("masterDataSource")DataSource slaveDataSource){
logger.info("create routing datasource...");
Map<Object, Object> map = new HashMap<>();
map.put("masterDataSource", masterDataSource);
map.put("slaveDataSource", slaveDataSource);
RoutingDataSource routing = new RoutingDataSource();
routing.setTargetDataSources(map);
routing.setDefaultTargetDataSource(masterDataSource);
return routing;
}
}
第二步:編寫RoutingDataSource
然后,我們用Spring內(nèi)置的RoutingDataSource,把兩個真實的數(shù)據(jù)源代理為一個動態(tài)數(shù)據(jù)源:
public class RoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return RoutingDataSourceContext.getDataSourceRoutingKey();
}
}
第三步:編寫RoutingDataSourceContext
用于存儲當(dāng)前需要切換為哪個數(shù)據(jù)源
public class RoutingDataSourceContext {
// holds data source key in thread local:
static final ThreadLocal<String> threadLocalDataSourceKey = new ThreadLocal<>();
public static String getDataSourceRoutingKey() {
String key = threadLocalDataSourceKey.get();
return key == null ? "masterDataSource" : key;
}
public RoutingDataSourceContext(String key) {
threadLocalDataSourceKey.set(key);
}
public void close() {
threadLocalDataSourceKey.remove();
}
}
測試(一下代碼為controller中代碼)
@GetMapping("/findAllProductM")
public String findAllProductM() {
String key = "masterDataSource";
RoutingDataSourceContext routingDataSourceContext = new RoutingDataSourceContext(key);
productService.findAllProductM();
return "master";
}
@GetMapping("/findAllProductS")
public String findAllProductS() {
String key = "slaveDataSource";
RoutingDataSourceContext routingDataSourceContext = new RoutingDataSourceContext(key);
productService.findAllProductS();
return "slave";
}
以上代碼即可實現(xiàn)數(shù)據(jù)源動態(tài)切換
優(yōu)化:
以上代碼是可行的,但是,需要讀數(shù)據(jù)庫的地方,就需要加上一大段RoutingDataSourceContext
ctx = ...代碼,使用起來十分不便。以下是優(yōu)化方案
我們可以申明一個自定義注解,將以上RoutingDataSourceContext中的值,放在注解的value屬性中,
然后定義一個切面類,當(dāng)我們在方法上標(biāo)注自定義注解的時候,執(zhí)行切面邏輯,獲取到注解中的值,set到RoutingDataSourceContext中,從而實現(xiàn)通過注解的方式,來動態(tài)切換數(shù)據(jù)源
以下是代碼實現(xiàn):
注解類
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RoutingWith {
String value() default "master";
}
切面類:
@Aspect
@Component
public class RoutingAspect {
@Around("@annotation(routingWith)")
public Object routingWithDataSource(ProceedingJoinPoint joinPoint, RoutingWith routingWith) throws Throwable {
String key = routingWith.value();
RoutingDataSourceContext ctx = new RoutingDataSourceContext(key);
return joinPoint.proceed();
}
}
改造Controller方法
@RoutingWith("masterDataSource")
@GetMapping("/findAllProductM")
public String findAllProductM() {
productService.findAllProductM(); return "lagou";
}
@RoutingWith("slaveDataSource")
@GetMapping("/findAllProductS")
public String findAllProductS() {
productService.findAllProductS(); return "lagou";
}
到此這篇關(guān)于詳解SpringBoot+Mybatis實現(xiàn)動態(tài)數(shù)據(jù)源切換的文章就介紹到這了,更多相關(guān)SpringBoot+Mybatis動態(tài)數(shù)據(jù)源切換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot動態(tài)數(shù)據(jù)源連接測試的操作詳解
- SpringBoot實現(xiàn)動態(tài)數(shù)據(jù)源切換的方法總結(jié)
- springboot配置多數(shù)據(jù)源(靜態(tài)和動態(tài)數(shù)據(jù)源)
- SpringBoot中動態(tài)數(shù)據(jù)源是實現(xiàn)與用途
- Springboot實現(xiàn)根據(jù)用戶ID切換動態(tài)數(shù)據(jù)源
- 如何在Java SpringBoot項目中配置動態(tài)數(shù)據(jù)源你知道嗎
- SpringBoot Mybatis動態(tài)數(shù)據(jù)源切換方案實現(xiàn)過程
- 通過springboot+mybatis+druid配置動態(tài)數(shù)據(jù)源
- SpringBoot整合MyBatisPlus配置動態(tài)數(shù)據(jù)源的方法
- springboot 動態(tài)數(shù)據(jù)源的實現(xiàn)方法(Mybatis+Druid)
- SpringBoot實現(xiàn)動態(tài)數(shù)據(jù)源切換的項目實踐
相關(guān)文章
IntelliJ IDEA 2020.1.2激活工具下載及破解方法免費(fèi)可用至2089年(強(qiáng)烈推薦)
這篇文章主要介紹了IntelliJ IDEA 2020.1.2激活工具下載及破解方法免費(fèi)可用至2089年(強(qiáng)烈推薦),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09
SpringBoot在項目停止(服務(wù)停止/關(guān)閉退出)之后執(zhí)行的方法
這篇文章主要給大家介紹了SpringBoot在項目停止(服務(wù)停止/關(guān)閉退出)之后執(zhí)行的兩種方法,實現(xiàn)DisposableBean接口和使用@PreDestroy注解,文中有詳細(xì)的代碼講解,具有一定的參考價值,需要的朋友可以參考下2023-12-12
Java批量轉(zhuǎn)換文件編碼格式的實現(xiàn)方法及實例代碼
這篇文章主要介紹了Java實現(xiàn) 批量轉(zhuǎn)換文件編碼格式的方法及實例代碼,本文通過實例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-04-04

