Spring Boot 項目中多數據源配置使用場景

在 Spring Boot 中配置多數據源是一個非常常見的需求,主要用于以下場景:

  • 讀寫分離:一個主數據庫(Master)負責寫操作,一個或多個從數據庫(Slave)負責讀操作,以提高性能和可用性。
  • 業務拆分:不同的業務模塊使用不同的數據庫(例如,用戶庫、訂單庫、商品庫)。
  • 連接異構數據庫:同時連接 MySQL、PostgreSQL 等不同類型的數據庫。

下面我將詳細介紹兩種主流的實現方式:

  1. 靜態方式(推薦用于業務隔離場景):通過包路徑區分不同的數據源,配置簡單,結構清晰。
  2. 動態方式(推薦用于讀寫分離場景):使用 AOP 和自定義注解,在方法級別動態切換數據源,更靈活。

方案一:靜態方式(按包路徑隔離)

這種方式的核心思想是為每個數據源創建一套獨立的配置(DataSource, SqlSessionFactory, TransactionManager),并使用 @MapperScan 注解掃描不同包路徑下的 Mapper 接口,將它們綁定到對應的數據源上。

1. 添加依賴 (pom.xml)

確保有以下依賴。通常 Spring Boot Starter 會包含大部分。

<dependencies><!-- Spring Boot Web Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- MyBatis-Plus Starter --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version> <!-- 請使用較新版本 --></dependency><!-- MySQL Driver --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!-- Connection Pool (HikariCP is default) --><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId></dependency>
</dependencies>
2. 配置文件 (application.yml)

為不同的數據源定義各自的連接信息,并用不同的前綴區分。

spring:datasource:# 主數據源配置 (master)master:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/db_master?serverTimezone=UTCusername: rootpassword: your_passwordtype: com.zaxxer.hikari.HikariDataSource # 指定連接池類型# 從數據源配置 (slave)slave:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3307/db_slave?serverTimezone=UTCusername: rootpassword: your_passwordtype: com.zaxxer.hikari.HikariDataSource
3. 創建數據源配置類

為每個數據源創建一個 Java 配置類。

主數據源配置 (MasterDataSourceConfig.java)

package com.example.config.datasource;import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;@Configuration
// 掃描 Master 庫的 Mapper 接口
@MapperScan(basePackages = "com.example.mapper.master", sqlSessionTemplateRef = "masterSqlSessionTemplate")
public class MasterDataSourceConfig {@Bean(name = "masterDataSource")@ConfigurationProperties(prefix = "spring.datasource.master")@Primary // 標記為主數據源public DataSource masterDataSource() {return DataSourceBuilder.create().build();}@Bean(name = "masterSqlSessionFactory")@Primarypublic SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception {MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();bean.setDataSource(dataSource);// 如果有 XML 文件,指定位置// bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/master/*.xml"));return bean.getObject();}@Bean(name = "masterTransactionManager")@Primarypublic DataSourceTransactionManager masterTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}@Bean(name = "masterSqlSessionTemplate")@Primarypublic SqlSessionTemplate masterSqlSessionTemplate(@Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {return new SqlSessionTemplate(sqlSessionFactory);}
}

從數據源配置 (SlaveDataSourceConfig.java)

package com.example.config.datasource;import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;@Configuration
// 掃描 Slave 庫的 Mapper 接口
@MapperScan(basePackages = "com.example.mapper.slave", sqlSessionTemplateRef = "slaveSqlSessionTemplate")
public class SlaveDataSourceConfig {@Bean(name = "slaveDataSource")@ConfigurationProperties(prefix = "spring.datasource.slave")public DataSource slaveDataSource() {return DataSourceBuilder.create().build();}@Bean(name = "slaveSqlSessionFactory")public SqlSessionFactory slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();bean.setDataSource(dataSource);// bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/slave/*.xml"));return bean.getObject();}@Bean(name = "slaveTransactionManager")public DataSourceTransactionManager slaveTransactionManager(@Qualifier("slaveDataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}@Bean(name = "slaveSqlSessionTemplate")public SqlSessionTemplate slaveSqlSessionTemplate(@Qualifier("slaveSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {return new SqlSessionTemplate(sqlSessionFactory);}
}
4. 創建 Mapper 接口

將不同數據源的 Mapper 接口放到各自的包下。

  • com.example.mapper.master -> UserMasterMapper.java
  • com.example.mapper.slave -> OrderSlaveMapper.java
5. 使用

現在你可以在 Service 中直接注入并使用對應的 Mapper,Spring 會自動為它們關聯正確的數據源。

@Service
public class MyService {@Autowiredprivate UserMasterMapper userMasterMapper; // 操作 master 庫@Autowiredprivate OrderSlaveMapper orderSlaveMapper; // 操作 slave 庫public void doSomething() {// ...userMasterMapper.insert(someUser); // 寫入主庫Order order = orderSlaveMapper.selectById(1); // 從從庫讀取}
}

優點:配置隔離,結構非常清晰,不會混淆。
缺點:如果一個 Service 方法需要同時操作兩個庫,代碼會稍微復雜,且默認的 @Transactional 不能跨數據源生效。


方案二:動態方式(AOP + 自定義注解)

這種方式更靈活,適用于讀寫分離等需要在同一個 Service 中切換數據源的場景。

1. 配置文件 (application.yml)

與方案一相同。

2. 創建自定義注解

創建一個注解,用于標記方法應該使用哪個數據源。

package com.example.config.dynamic;import java.lang.annotation.*;@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {String value() default "master"; // 默認使用 master 數據源
}
3. 創建數據源上下文持有者

使用 ThreadLocal 來存儲當前線程需要使用的數據源 Key。

package com.example.config.dynamic;public class DataSourceContextHolder {private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();public static void setDataSourceKey(String key) {CONTEXT_HOLDER.set(key);}public static String getDataSourceKey() {return CONTEXT_HOLDER.get();}public static void clearDataSourceKey() {CONTEXT_HOLDER.remove();}
}
4. 創建動態數據源類

繼承 AbstractRoutingDataSource,重寫 determineCurrentLookupKey 方法,從 DataSourceContextHolder 獲取當前數據源 Key。

package com.example.config.dynamic;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DataSourceContextHolder.getDataSourceKey();}
}
5. 創建AOP切面

創建一個切面,攔截 @DataSource 注解,在方法執行前設置數據源 Key,在方法執行后清除它。

package com.example.config.dynamic;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;@Aspect
@Component
@Order(1) // 保證該AOP在@Transactional之前執行
public class DataSourceAspect {@Pointcut("@annotation(com.example.config.dynamic.DataSource)")public void dsPointCut() {}@Around("dsPointCut()")public Object around(ProceedingJoinPoint point) throws Throwable {MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();DataSource dataSource = method.getAnnotation(DataSource.class);// 設置數據源if (dataSource != null) {DataSourceContextHolder.setDataSourceKey(dataSource.value());}try {return point.proceed();} finally {// 清除數據源,防止內存泄漏DataSourceContextHolder.clearDataSourceKey();}}
}
6. 整合數據源配置

創建一個統一的配置類來管理所有數據源。

package com.example.config;import com.example.config.dynamic.DynamicDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;@Configuration
public class DynamicDataSourceConfig {@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 DynamicDataSource dataSource(DataSource masterDataSource, 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); // 設置默認數據源return dynamicDataSource;}
}

注意:這種方式下,SqlSessionFactoryTransactionManager 只需要配置一個,并讓它們使用這個 @PrimaryDynamicDataSource 即可。Spring Boot 會自動配置好。

7. 使用

在 Service 方法上添加 @DataSource 注解來切換數據源。

@Service
public class ProductService {@Autowiredprivate ProductMapper productMapper;// 默認不加注解,使用 master 數據源(因為我們配置了默認值)@Transactional // 事務仍然有效public void addProduct(Product product) {productMapper.insert(product);}// 顯式指定使用 slave 數據源@DataSource("slave")public Product getProductById(Integer id) {return productMapper.selectById(id);}
}

重要提醒:關于事務

  • 單數據源事務:在以上兩種方案中,只要 DataSourceTransactionManagerSqlSessionFactory 綁定的是同一個 DataSource@Transactional 注解就能正常工作。在動態方案中,事務管理器綁定的是 DynamicDataSource,它能確保事務在當前線程選擇的數據源上生效。
  • 跨數據源事務(分布式事務):標準的 @Transactional 無法管理跨多個數據源的事務。如果你需要在同一個方法中對 masterslave 都進行寫操作,并保證它們的原子性,你需要引入 JTA(Java Transaction API)事務管理器,例如 AtomikosNarayana。這會增加配置的復雜度。

總結

  • 如果你的業務模塊和數據庫綁定關系固定,方案一(靜態方式) 更簡單、更直觀。
  • 如果你需要實現讀寫分離,或者在業務邏輯中頻繁切換數據源,方案二(動態方式) 提供了更高的靈活性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/88181.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/88181.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/88181.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

FAAC 在海思平臺使用得到aac實時音頻流

FAAC 在海思平臺使用得到aac實時音頻流 使用 FAAC將音頻 pcm轉為 aac 主要參見這篇博客 FAAC 在君正平臺使用得到aac實時音頻流_君正 x2600 音頻-CSDN博客

javascript函數參數類似python函數參數星號*解耦數組

序言通常情況下&#xff0c;我們很可能不清楚參數有多少&#xff0c;這個時候用的都是數組。但是使用數組和單個元素&#xff0c;從內心情感來說&#xff0c;它們是兩種維度&#xff0c;為了讓參數成為一個數組&#xff0c;把單個輸入的參數強加一個數組的外殼&#xff0c;并不…

C語言基礎(1)

1.編譯器的選擇 我們的c語言是一門&#xff0c;我們寫的c語言代碼是文本文件(存放在.c為后綴的文件中)&#xff0c;文本文件本身無法被執行&#xff0c;必須通過編譯器的編譯和鏈接器的鏈接&#xff0c;生成可執行的二進制文件&#xff0c;才能夠被執行注意&#xff1a; 每個源…

Rust賦能美團云原生DevOps實踐

Rust 云原生 DevOps 實踐 在云原生環境中,Rust 的高性能與安全性使其成為構建微服務和基礎設施工具的理想選擇。Docker 作為容器化標準工具,結合 Rust 的跨平臺特性,可高效實現持續集成與部署(CI/CD)。 構建優化的 Rust Docker 鏡像 多階段構建是 Rust 項目容器化的關鍵…

計算機網絡實驗——配置ACL

ACL基礎一、實驗目的1. 配置H3C路由器基本ACL。二、實驗要求1. 熟練掌握網絡配置能力。2. 熟練掌握ACL基本配置。三、實驗步驟&#xff08;1&#xff09;使用reset saved-configuration命令和reboot命令&#xff0c;重置路由器原有配置&#xff0c;如圖1所示。圖 1&#xff08;…

在本地部署mcp服務器實現自然語言操作mysql數據庫,輕松實現數據表的增~ 刪~ 改~ 查~

1.將寫好的mcp_server代碼放在本地任意盤&#xff01; import asyncio import logging import os import sys from mysql.connector import connect, Error from mcp.server import Server from mcp.types import Resource, Tool, TextContent from pydantic import AnyUrl# Co…

2025快手創作者中心發布視頻python實現

難度還行&#xff0c;只有一個__NS_sig3加密&#xff0c;流程麻煩點cookies_list cookie.split("; ")cookie_dict {}# 遍歷每個 Cookie&#xff0c;根據等號將鍵值對拆分并添加到字典中for cookie in cookies_list:key_value cookie.split("")if len(ke…

Android 組件內核

文章目錄什么是binder1. 什么是Binder&#xff1f;2. Binder架構組成3. 工作原理與通信流程1&#xff09;服務注冊2&#xff09;服務查詢3&#xff09;通信過程4&#xff09;核心數據結構4. 關鍵技術點5. 常見面試考點1&#xff09;Binder與傳統IPC&#xff08;Socket、管道、共…

java類加載機制:Tomcat的類加載機制

Tomcat類加載機制深度解析&#xff1a;打破雙親委派的Web容器實現 Tomcat作為Java Web容器&#xff0c;其類加載機制為滿足Web應用的隔離性、熱部署和兼容性需求&#xff0c;對標準Java類加載機制進行了定制化擴展&#xff0c;核心是打破雙親委派模型并引入多層級類加載器。以下…

【PTA數據結構 | C語言版】從順序表 list 中刪除第 i 個元素

本專欄持續輸出數據結構題目集&#xff0c;歡迎訂閱。 文章目錄題目代碼題目 請編寫程序&#xff0c;將 n 個整數存入順序表&#xff0c;對任一指定的第 i 個位置&#xff0c;將這個位置上的元素從順序表中刪除。注意&#xff1a;i 代表位序&#xff0c;從 1 開始&#xff0c;…

VS2022 C++ EasyX庫 掃雷游戲項目開發:打造經典游戲的詳細之旅

老樣子&#xff0c;先上效果 視頻演示 C經典掃雷-介紹一、引言 在這篇博客中&#xff0c;我將詳細介紹掃雷游戲項目的開發過程。掃雷作為一款經典的游戲&#xff0c;其規則簡單但富有挑戰性。通過開發這個項目&#xff0c;我不僅加深了對 C 編程的理解&#xff0c;還提升了自己…

Go語言網絡游戲服務器模塊化編程

本文以使用origin框架&#xff08;一款使用Go語言寫的開源游戲服務器框架&#xff09;為例進行說明&#xff0c;當然也可以使用其它的框架或者自己寫。 在框架中PBProcessor用來處理Protobuf消息&#xff0c;在使用之前&#xff0c;需要使用Register函數注冊網絡消息&#xff…

【機器人】Aether 多任務世界模型 | 4D動態重建 | 視頻預測 | 視覺規劃

Aether 是一個的世界模型&#xff0c;整合幾何重建與生成建模的統一框架&#xff0c;實現類人空間推理能力。 來自ICCV 2025&#xff0c;該框架具有三大核心功能&#xff1a; (1) 4D動態重建&#xff0c;(2) 動作條件視頻預測&#xff0c; (3) 目標條件視覺規劃。 代碼地址&…

MiniMind:3小時訓練26MB微型語言模型,開源項目助力AI初學者快速入門

開發&#xff5c;界面&#xff5c;引擎&#xff5c;交付&#xff5c;副駕——重寫全棧法則&#xff1a;AI原生的倍速造應用流來自全棧程序員 nine 的探索與實踐&#xff0c;持續迭代中。 歡迎關注評論私信交流~ 在大型語言模型(LLaMA、GPT等)日益流行的今天&#xff0c;一個名為…

相機Camera日志實例分析之五:相機Camx【萌拍閃光燈后置拍照】單幀流程日志詳解

【關注我&#xff0c;后續持續新增專題博文&#xff0c;謝謝&#xff01;&#xff01;&#xff01;】 上一篇我們講了&#xff1a; 這一篇我們開始講&#xff1a; 目錄 一、場景操作步驟 二、日志基礎關鍵字分級如下 三、場景日志如下&#xff1a; 一、場景操作步驟 操作步…

[2-02-02].第03節:環境搭建 - Win10搭建ES集群環境

ElasticSearch學習大綱 基于ElasticSearch7.8版本 一、ElasticStack下載&#xff1a; 1.Elasticsearch 的官方地址 2.Elasticsearch 下載地址&#xff1a; 二、集群搭建: 第1步&#xff1a;創建es目錄&#xff1a; 1.創建 elasticsearch-cluster 文件夾&#xff0c;在內部…

操作系統核心技術剖析:從Android驅動模型到鴻蒙微內核的國產化實踐

目錄 一、移動端操作系統技術細節 1. Android 內核版本 核心模塊 驅動架構 國內定制案例 2. iOS XNU內核關鍵模塊 安全機制 3. HarmonyOS 多內核架構 驅動隔離 二、PC端操作系統技術細節 1. Windows NT內核 模塊分層 驅動模型 國內適配 2. macOS&#xff08;X…

整合Spring、Spring MVC與MyBatis:構建高效Java Web應用

本文將詳細講解如何整合Spring、Spring MVC和MyBatis&#xff08;SSM框架&#xff09;&#xff0c;通過一個人員信息查詢案例展示完整開發流程。所有代碼基于提供的文件實現。一、項目結構src/ ├── main/ │ ├── java/ │ │ └── com/ │ │ └── qcb…

視頻插幀技術:從流暢觀影到AI創作的革命

一、起源&#xff1a;為什么需要視頻插幀&#xff1f; 視頻的本質是連續播放的靜態幀序列&#xff0c;幀率&#xff08;FPS&#xff09; 決定了流暢度。早期電影受限于拍攝技術和存儲成本&#xff0c;普遍采用24FPS&#xff0c;而現代顯示設備&#xff08;如120Hz屏幕&#xf…

【一起來學AI大模型】PyTorch 實戰示例:使用 BatchNorm 處理張量(Tensor)

PyTorch 實戰示例 演示如何在神經網絡中使用 BatchNorm 處理張量&#xff08;Tensor&#xff09;&#xff0c;涵蓋關鍵實現細節和常見陷阱。示例包含數據準備、模型構建、訓練/推理模式切換及結果分析。示例場景&#xff1a;在 CIFAR-10 數據集上實現帶 BatchNorm 的 CNNimport…