最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot項目中多數(shù)據(jù)源配置方法與使用場景

 更新時間:2025年07月10日 08:19:43   作者:冰糖心書房  
在 Spring Boot 中配置多數(shù)據(jù)源是一個非常常見的需求,本文將為大家詳細介紹兩種主流的實現(xiàn)方式與使用場景,感興趣的小伙伴可以跟隨小編一起學習一下

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

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

下面我將詳細介紹兩種主流的實現(xiàn)方式:

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

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

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

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)

為不同的數(shù)據(jù)源定義各自的連接信息,并用不同的前綴區(qū)分。

spring:
  datasource:
    # 主數(shù)據(jù)源配置 (master)
    master:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/db_master?serverTimezone=UTC
      username: root
      password: your_password
      type: com.zaxxer.hikari.HikariDataSource # 指定連接池類型

    # 從數(shù)據(jù)源配置 (slave)
    slave:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3307/db_slave?serverTimezone=UTC
      username: root
      password: your_password
      type: com.zaxxer.hikari.HikariDataSource

3. 創(chuàng)建數(shù)據(jù)源配置類

為每個數(shù)據(jù)源創(chuàng)建一個 Java 配置類。

主數(shù)據(jù)源配置 (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 // 標記為主數(shù)據(jù)源
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "masterSqlSessionFactory")
    @Primary
    public 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")
    @Primary
    public DataSourceTransactionManager masterTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "masterSqlSessionTemplate")
    @Primary
    public SqlSessionTemplate masterSqlSessionTemplate(@Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

從數(shù)據(jù)源配置 (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. 創(chuàng)建 Mapper 接口

將不同數(shù)據(jù)源的 Mapper 接口放到各自的包下。

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

5. 使用

現(xiàn)在你可以在 Service 中直接注入并使用對應的 Mapper,Spring 會自動為它們關聯(lián)正確的數(shù)據(jù)源。

@Service
public class MyService {
    @Autowired
    private UserMasterMapper userMasterMapper; // 操作 master 庫

    @Autowired
    private OrderSlaveMapper orderSlaveMapper; // 操作 slave 庫

    public void doSomething() {
        // ...
        userMasterMapper.insert(someUser); // 寫入主庫
        Order order = orderSlaveMapper.selectById(1); // 從從庫讀取
    }
}

優(yōu)點:配置隔離,結構非常清晰,不會混淆。

缺點:如果一個 Service 方法需要同時操作兩個庫,代碼會稍微復雜,且默認的 @Transactional 不能跨數(shù)據(jù)源生效。

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

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

1. 配置文件 (application.yml)

與方案一相同。

2. 創(chuàng)建自定義注解

創(chuàng)建一個注解,用于標記方法應該使用哪個數(shù)據(jù)源。

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 數(shù)據(jù)源
}

3. 創(chuàng)建數(shù)據(jù)源上下文持有者

使用 ThreadLocal 來存儲當前線程需要使用的數(shù)據(jù)源 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. 創(chuàng)建動態(tài)數(shù)據(jù)源類

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

package com.example.config.dynamic;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceKey();
    }
}

5. 創(chuàng)建AOP切面

創(chuàng)建一個切面,攔截 @DataSource 注解,在方法執(zhí)行前設置數(shù)據(jù)源 Key,在方法執(zhí)行后清除它。

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之前執(zhí)行
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);
        
        // 設置數(shù)據(jù)源
        if (dataSource != null) {
            DataSourceContextHolder.setDataSourceKey(dataSource.value());
        }

        try {
            return point.proceed();
        } finally {
            // 清除數(shù)據(jù)源,防止內存泄漏
            DataSourceContextHolder.clearDataSourceKey();
        }
    }
}

6. 整合數(shù)據(jù)源配置

創(chuàng)建一個統(tǒng)一的配置類來管理所有數(shù)據(jù)源。

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 // 必須!將動態(tài)數(shù)據(jù)源設置為主數(shù)據(jù)源
    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); // 設置默認數(shù)據(jù)源
        return dynamicDataSource;
    }
}

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

7. 使用

在 Service 方法上添加 @DataSource 注解來切換數(shù)據(jù)源。

@Service
public class ProductService {

    @Autowired
    private ProductMapper productMapper;

    // 默認不加注解,使用 master 數(shù)據(jù)源(因為我們配置了默認值)
    @Transactional // 事務仍然有效
    public void addProduct(Product product) {
        productMapper.insert(product);
    }

    // 顯式指定使用 slave 數(shù)據(jù)源
    @DataSource("slave")
    public Product getProductById(Integer id) {
        return productMapper.selectById(id);
    }
}

重要提醒:關于事務

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

總結

如果你的業(yè)務模塊和數(shù)據(jù)庫綁定關系固定,方案一(靜態(tài)方式) 更簡單、更直觀。

如果你需要實現(xiàn)讀寫分離,或者在業(yè)務邏輯中頻繁切換數(shù)據(jù)源,方案二(動態(tài)方式) 提供了更高的靈活性。

到此這篇關于SpringBoot項目中多數(shù)據(jù)源配置方法與使用場景的文章就介紹到這了,更多相關SpringBoot多數(shù)據(jù)源配置內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • java驗證電話號碼的方法

    java驗證電話號碼的方法

    這篇文章主要介紹了java驗證電話號碼的方法,需要的朋友可以參考下
    2014-02-02
  • java單例模式使用詳解

    java單例模式使用詳解

    這篇文章主要介紹了java單例模式使用方法,通常它的實現(xiàn)方式分為懶漢式實現(xiàn)和餓漢式,下面是詳細說明加示例,需要的朋友可以參考下
    2014-04-04
  • spring aop之@AfterReturning不生效問題及解決

    spring aop之@AfterReturning不生效問題及解決

    這篇文章主要介紹了spring aop之@AfterReturning不生效問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • Java?中很好用的數(shù)據(jù)結構EnumSet

    Java?中很好用的數(shù)據(jù)結構EnumSet

    這篇文章主要介紹了Java?中很好用的數(shù)據(jù)結構EnumSet,EnumMap即屬于一個Map,下文圍繞主題展開詳細內容,需要的小伙伴可以參考參考一下
    2022-05-05
  • Java使用NioSocket手動實現(xiàn)HTTP服務器

    Java使用NioSocket手動實現(xiàn)HTTP服務器

    本篇文章主要介紹了Java使用NioSocket手動實現(xiàn)HTTP服務器,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-05-05
  • MyBatis實現(xiàn)物理分頁的實例

    MyBatis實現(xiàn)物理分頁的實例

    這篇文章主要介紹了MyBatis實現(xiàn)物理分頁的實例,MyBatis使用RowBounds實現(xiàn)的分頁是邏輯分頁,有興趣的可以了解一下。
    2017-01-01
  • java代碼之謎運算符篇

    java代碼之謎運算符篇

    從最簡單的運算符加號(+)說起,加號(+)是個二元運算符——也就是說,加號只把兩個數(shù)聯(lián)接起來,從來不把第三個或者更多的聯(lián)接起來
    2012-11-11
  • Java數(shù)據(jù)結構之有向圖設計與實現(xiàn)詳解

    Java數(shù)據(jù)結構之有向圖設計與實現(xiàn)詳解

    有向圖是具有方向性的圖,由一組頂點和一組有方向的邊組成,每條方向的邊都連著一對有序的頂點。本文為大家介紹的是有向圖的設計與實現(xiàn),需要的可以參考一下
    2022-11-11
  • Java關于含有繼承類的成員初始化過程講解

    Java關于含有繼承類的成員初始化過程講解

    今天小編就為大家分享一篇Java關于含有繼承類的成員初始化過程講解,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-04-04
  • Spring Cloud Gateway 服務網(wǎng)關快速實現(xiàn)解析

    Spring Cloud Gateway 服務網(wǎng)關快速實現(xiàn)解析

    這篇文章主要介紹了Spring Cloud Gateway 服務網(wǎng)關快速實現(xiàn)解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-08-08

最新評論

共和县| 徐闻县| 介休市| 江华| 毕节市| 青浦区| 叙永县| 晋宁县| 四川省| 桃园市| 建昌县| 老河口市| 七台河市| 梁平县| 丹棱县| 无极县| 瑞昌市| 潜江市| 钟山县| 镇康县| 兴义市| 阿荣旗| 无极县| 湘潭市| 内乡县| 洛阳市| 临澧县| 邹城市| 齐河县| 班玛县| 闸北区| 平和县| 抚松县| 台东县| 丹凤县| 开封县| 富平县| 双牌县| 图木舒克市| 龙海市| 仙桃市|