MyBatis動(dòng)態(tài)數(shù)據(jù)源切換的完整方案
在微服務(wù)架構(gòu)和分布式系統(tǒng)中,動(dòng)態(tài)數(shù)據(jù)源是應(yīng)對(duì)多環(huán)境切換、讀寫分離、多租戶等復(fù)雜場(chǎng)景的核心工具。MyBatis 與 Spring 結(jié)合后,通過 AbstractRoutingDataSource 和 線程上下文管理(ThreadLocal),可以靈活實(shí)現(xiàn)數(shù)據(jù)源的動(dòng)態(tài)切換。以下是實(shí)現(xiàn) MyBatis 動(dòng)態(tài)數(shù)據(jù)源的完整方案,涵蓋核心步驟、代碼示例和最佳實(shí)踐。
1. 核心概念
- 動(dòng)態(tài)數(shù)據(jù)源:根據(jù)業(yè)務(wù)需求自動(dòng)選擇不同的數(shù)據(jù)庫連接(如主庫、從庫、測(cè)試庫)。
AbstractRoutingDataSource:Spring 提供的抽象類,負(fù)責(zé)動(dòng)態(tài)決定當(dāng)前使用哪個(gè)數(shù)據(jù)源。ThreadLocal:線程安全的上下文管理工具,保存當(dāng)前線程的數(shù)據(jù)源標(biāo)識(shí)(如master、slave)。
2. 實(shí)現(xiàn)步驟
(1) 配置多個(gè)數(shù)據(jù)源
在 application.yml 或 application.properties 中定義多個(gè)數(shù)據(jù)源配置:
spring:
datasource:
master:
url: jdbc:mysql://localhost:3306/master_db
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
slave:
url: jdbc:mysql://localhost:3306/slave_db
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
(2) 創(chuàng)建動(dòng)態(tài)數(shù)據(jù)源類
繼承 AbstractRoutingDataSource,重寫 determineCurrentLookupKey 方法:
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// 從線程上下文中獲取當(dāng)前數(shù)據(jù)源標(biāo)識(shí)
return DataSourceContextHolder.getDataSource();
}
}
(3) 數(shù)據(jù)源上下文管理
使用 ThreadLocal 保存當(dāng)前線程的數(shù)據(jù)源標(biāo)識(shí):
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSource(String dataSource) {
contextHolder.set(dataSource);
}
public static String getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}
(4) 配置動(dòng)態(tài)數(shù)據(jù)源 Bean
在 Spring 配置類中定義動(dòng)態(tài)數(shù)據(jù)源,并綁定多個(gè)數(shù)據(jù)源:
@Configuration
public class DataSourceConfig {
@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@Primary
public DataSource dynamicDataSource(
@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slaveDataSource") DataSource slaveDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", masterDataSource);
targetDataSources.put("slave", slaveDataSource);
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.setDefaultTargetDataSource(masterDataSource); // 默認(rèn)使用主數(shù)據(jù)源
return dynamicDataSource;
}
}
(5) 整合 MyBatis
配置 SqlSessionFactory 和 MapperScannerConfigurer,確保使用動(dòng)態(tài)數(shù)據(jù)源:
@Configuration
@MapperScan(basePackages = "com.example.mapper")
public class MyBatisConfig {
@Autowired
private DataSource dynamicDataSource;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dynamicDataSource);
return factoryBean.getObject();
}
}
(6) 動(dòng)態(tài)切換數(shù)據(jù)源
在業(yè)務(wù)代碼中通過 DataSourceContextHolder 設(shè)置數(shù)據(jù)源標(biāo)識(shí):
// 切換到從數(shù)據(jù)源(讀操作)
DataSourceContextHolder.setDataSource("slave");
try {
// 執(zhí)行查詢操作
List<User> users = userMapper.selectAll();
} finally {
// 清除線程上下文
DataSourceContextHolder.clearDataSource();
}
// 切換到主數(shù)據(jù)源(寫操作)
DataSourceContextHolder.setDataSource("master");
try {
userMapper.insert(user);
} finally {
DataSourceContextHolder.clearDataSource();
}
(7) 事務(wù)管理
為每個(gè)數(shù)據(jù)源配置獨(dú)立的事務(wù)管理器,并通過 @Transactional 注解指定事務(wù)管理器:
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
@Bean(name = "masterTransactionManager")
public PlatformTransactionManager masterTransactionManager(
@Qualifier("masterDataSource") DataSource masterDataSource) {
return new DataSourceTransactionManager(masterDataSource);
}
@Bean(name = "slaveTransactionManager")
public PlatformTransactionManager slaveTransactionManager(
@Qualifier("slaveDataSource") DataSource slaveDataSource) {
return new DataSourceTransactionManager(slaveDataSource);
}
}
// 在 Service 層指定事務(wù)管理器
@Service
public class UserService {
@Transactional("masterTransactionManager")
public void insertUser(User user) {
userMapper.insert(user);
}
@Transactional("slaveTransactionManager")
public List<User> selectAllUsers() {
return userMapper.selectAll();
}
}
3. 使用場(chǎng)景
讀寫分離
- 寫操作(
INSERT/UPDATE/DELETE)使用主庫,讀操作(SELECT)使用從庫。 - 示例:訂單寫入主庫,訂單查詢從庫。
多環(huán)境切換
- 開發(fā)環(huán)境、測(cè)試環(huán)境、生產(chǎn)環(huán)境分別綁定不同的數(shù)據(jù)源。
- 示例:通過配置文件切換不同數(shù)據(jù)庫地址。
多租戶隔離
- 每個(gè)租戶使用獨(dú)立的數(shù)據(jù)源,通過租戶標(biāo)識(shí)動(dòng)態(tài)切換。
- 示例:企業(yè)級(jí) SaaS 系統(tǒng),不同企業(yè)訪問不同數(shù)據(jù)庫。
4. 注意事項(xiàng)
線程安全
- 確保
ThreadLocal在異步任務(wù)中傳遞數(shù)據(jù)源標(biāo)識(shí)(如使用InheritableThreadLocal或TransmittableThreadLocal)。
事務(wù)一致性
- 跨數(shù)據(jù)源的事務(wù)需謹(jǐn)慎處理,避免臟讀或數(shù)據(jù)不一致??墒褂梅植际绞聞?wù)框架(如 Seata)。
性能優(yōu)化
- 避免頻繁切換數(shù)據(jù)源,合理設(shè)計(jì)讀寫分離策略。
異常處理
- 捕獲數(shù)據(jù)源切換異常,提供回退機(jī)制(如默認(rèn)使用主數(shù)據(jù)源)。
5. 總結(jié)
| 特性 | 實(shí)現(xiàn)方式 |
|---|---|
| 動(dòng)態(tài)切換 | AbstractRoutingDataSource + ThreadLocal |
| 讀寫分離 | 通過業(yè)務(wù)邏輯判斷操作類型,設(shè)置 master/slave |
| 多環(huán)境支持 | 通過配置文件區(qū)分不同環(huán)境的數(shù)據(jù)源 |
| 事務(wù)管理 | 為每個(gè)數(shù)據(jù)源配置獨(dú)立的 PlatformTransactionManager |
| 線程安全 | 使用 ThreadLocal 保存上下文,異步場(chǎng)景需傳遞上下文 |
通過以上方案,MyBatis 動(dòng)態(tài)數(shù)據(jù)源可以靈活適配多種業(yè)務(wù)場(chǎng)景,成為多環(huán)境切換的“神器”。結(jié)合 Spring 的強(qiáng)大生態(tài),開發(fā)者可以輕松實(shí)現(xiàn)高可用、高性能的分布式系統(tǒng)。
以上就是MyBatis動(dòng)態(tài)數(shù)據(jù)源切換的完整方案的詳細(xì)內(nèi)容,更多關(guān)于MyBatis動(dòng)態(tài)數(shù)據(jù)源切換的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
全面詳解java代碼重構(gòu)與設(shè)計(jì)模式
這篇文章主要為大家介紹了全面詳解java代碼重構(gòu)與設(shè)計(jì)模式的全面詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06
SpringBoot實(shí)現(xiàn)多租戶系統(tǒng)架構(gòu)的5種設(shè)計(jì)方案介紹
多租戶(Multi-tenancy)是一種軟件架構(gòu)模式,允許單個(gè)應(yīng)用實(shí)例服務(wù)于多個(gè)客戶(租戶),同時(shí)保持租戶數(shù)據(jù)的隔離性和安全性,本文分享了SpringBoot環(huán)境下實(shí)現(xiàn)多租戶系統(tǒng)的5種架構(gòu)設(shè)計(jì)方案,需要的可以參考一下2025-05-05
關(guān)于String轉(zhuǎn)Json的幾種方式
這篇文章主要介紹了關(guān)于String轉(zhuǎn)Json的幾種方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
基于JavaMail實(shí)現(xiàn)郵件發(fā)送
這篇文章主要為大家詳細(xì)介紹了基于JavaMail實(shí)現(xiàn)郵件發(fā)送功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03
java 使用簡單的demo實(shí)例告訴你優(yōu)化算法的強(qiáng)大
本篇文章介紹了,在java中使用簡單的demo實(shí)例告訴你優(yōu)化算法的強(qiáng)大。需要的朋友參考下2013-05-05
Mybatis-plus對(duì)單表操作的封裝實(shí)現(xiàn)
本文詳細(xì)介紹了MyBatis-Plus單表操作,包括自定義SQL、邏輯刪除、樂觀鎖、全局?jǐn)r截器和代碼生成器等,具有一定的參考價(jià)值,感興趣的可以了解一下2024-12-12
java與C 代碼運(yùn)行效率的對(duì)比(整理)
最近和朋友無意間討論起了 有關(guān)java 和C 的 效率問題, (我是java 推介者, 他是 c 語言推介者, 他做的是嵌入式)故,想通過網(wǎng)絡(luò)查詢一下, 總結(jié)一下,兩者到底效率如何,其有何差異,原因又是啥?各種優(yōu)勢(shì)有在何處?2021-04-04

