欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

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

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

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

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

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

這種方式的核心思想是為每個數(shù)據(jù)源創(chuàng)建一套獨立的配置(DataSource, SqlSessionFactory, TransactionManager),并使用 @MapperScan 注解掃描不同包路徑下的 Mapper 接口,將它們綁定到對應(yīng)的數(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 中直接注入并使用對應(yīng)的 Mapper,Spring 會自動為它們關(guān)聯(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)點:配置隔離,結(jié)構(gòu)非常清晰,不會混淆。

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

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

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

1. 配置文件 (application.yml)

與方案一相同。

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

創(chuàng)建一個注解,用于標記方法應(yī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 來存儲當(dāng)前線程需要使用的數(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 獲取當(dāng)前數(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è)置數(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è)置數(shù)據(jù)源
        if (dataSource != null) {
            DataSourceContextHolder.setDataSourceKey(dataSource.value());
        }

        try {
            return point.proceed();
        } finally {
            // 清除數(shù)據(jù)源,防止內(nèi)存泄漏
            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è)置為主數(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è)置默認數(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 // 事務(wù)仍然有效
    public void addProduct(Product product) {
        productMapper.insert(product);
    }

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

重要提醒:關(guān)于事務(wù)

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

總結(jié)

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

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

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

相關(guān)文章

  • Java中的權(quán)重算法(如Dubbo的負載均衡權(quán)重)詳解

    Java中的權(quán)重算法(如Dubbo的負載均衡權(quán)重)詳解

    這篇文章主要介紹了Java中的權(quán)重算法(如Dubbo的負載均衡權(quán)重)詳解,負載均衡,其含義就是指將負載進行平衡、分攤到多個操作單元上進行運行,例如FTP服務(wù)器、Web服務(wù)器、企業(yè)核心應(yīng)用服務(wù)器和其它主要任務(wù)服務(wù)器等,從而協(xié)同完成工作任務(wù),需要的朋友可以參考下
    2023-08-08
  • SpringBoot詳細講解靜態(tài)資源導(dǎo)入的實現(xiàn)

    SpringBoot詳細講解靜態(tài)資源導(dǎo)入的實現(xiàn)

    在Web開發(fā)過程中,我們需要接觸許多靜態(tài)資源,如CSS、JS、圖片等;在之前的開發(fā)中,這些資源都放在Web目錄下,用到的時候按照對應(yīng)路徑訪問即可。不過在SpringBoot項目中,沒有了Web目錄,那這些靜態(tài)資源該放到哪里去,又要如何訪問呢?這就是我們要講的靜態(tài)資源導(dǎo)入
    2022-05-05
  • SpringMVC實現(xiàn)上傳下載文件

    SpringMVC實現(xiàn)上傳下載文件

    這篇文章主要為大家詳細介紹了SpringMVC實現(xiàn)上傳下載文件,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • java必學(xué)必會之GUI編程

    java必學(xué)必會之GUI編程

    這篇文章主要為大家詳細介紹了java GUI編程,對于GUI編程小編也不是很了解,通過這篇文章和大家一起學(xué)習(xí)GUI編程,感興趣的小伙伴們可以參考一下
    2015-12-12
  • IDEA?報Plugin'maven-resources-plugin:'not?found?的解決方案

    IDEA?報Plugin'maven-resources-plugin:'not?found?

    如果在使用?IDEA?時遇到?"Plugin?'maven-resources-plugin:'?not?found"?錯誤,可能是由于?Maven?倉庫中未找到所需的?Maven?插件,近小編給大家分享幾種解決方法,感興趣的朋友跟隨小編一起看看吧
    2023-07-07
  • Java 深入淺出講解代理模式

    Java 深入淺出講解代理模式

    代理模式是Java常見的設(shè)計模式之一。所謂代理模式是指客戶端并不直接調(diào)用實際的對象,而是通過調(diào)用代理,來間接的調(diào)用實際的對象
    2022-03-03
  • java ThreadLocal使用案例詳解

    java ThreadLocal使用案例詳解

    這篇文章主要為大家詳細介紹了java ThreadLocal的使用案例,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-03-03
  • java實現(xiàn)學(xué)生宿舍系統(tǒng)

    java實現(xiàn)學(xué)生宿舍系統(tǒng)

    這篇文章主要為大家詳細介紹了java實現(xiàn)學(xué)生宿舍系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Redis如何實現(xiàn)分布式鎖詳解

    Redis如何實現(xiàn)分布式鎖詳解

    分布式鎖一般有三種實現(xiàn)方式:1. 數(shù)據(jù)庫樂觀鎖;2. 基于Redis的分布式鎖;3. 基于ZooKeeper的分布式鎖.本篇文章將介紹第二種方式,基于Redis實現(xiàn)分布式鎖,文中有非常詳細的介紹,需要的朋友可以參考下
    2021-06-06
  • Java.lang.Long.parseLong()方法詳解及示例

    Java.lang.Long.parseLong()方法詳解及示例

    這個java.lang.Long.parseLong(String s) 方法解析字符串參數(shù)s作為有符號十進制長,下面這篇文章主要給大家介紹了關(guān)于Java.lang.Long.parseLong()方法詳解及示例的相關(guān)資料,需要的朋友可以參考下
    2023-01-01

最新評論