欧美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 // 標(biāo)記為主數(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ù)雜,且默認(rèn)的 @Transactional 不能跨數(shù)據(jù)源生效。

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

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

1. 配置文件 (application.yml)

與方案一相同。

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

創(chuàng)建一個注解,用于標(biāo)記方法應(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"; // 默認(rèn)使用 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è)置默認(rèn)數(shù)據(jù)源
        return dynamicDataSource;
    }
}

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

7. 使用

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

@Service
public class ProductService {

    @Autowired
    private ProductMapper productMapper;

    // 默認(rèn)不加注解,使用 master 數(shù)據(jù)源(因為我們配置了默認(rèn)值)
    @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ù)):標(biāo)準(zhǔn)的 @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文件IO操作教程之DirectIO的意義

    Java文件IO操作教程之DirectIO的意義

    這篇文章主要給大家介紹了關(guān)于Java文件IO操作教程之DirectIO的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • springboot項目賬戶注冊邏輯功能實現(xiàn)

    springboot項目賬戶注冊邏輯功能實現(xiàn)

    這篇文章主要介紹了springboot項目賬戶注冊邏輯功能實現(xiàn),本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友一起看看吧
    2024-12-12
  • 解析Spring中的靜態(tài)代理和動態(tài)代理

    解析Spring中的靜態(tài)代理和動態(tài)代理

    學(xué)習(xí) Spring 的過程中,不可避免要掌握代理模式。這篇文章總結(jié)一下代理模式。顧名思義,代理,就是你委托別人幫你辦事,所以代理模式也有人稱作委托模式的。比如領(lǐng)導(dǎo)要做什么事,可以委托他的秘書去幫忙做,這時就可以把秘書看做領(lǐng)導(dǎo)的代理
    2021-06-06
  • Java處理UnresolvedAddressException異常的問題及解決

    Java處理UnresolvedAddressException異常的問題及解決

    這篇文章主要介紹了Java處理UnresolvedAddressException異常的問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-05-05
  • 一文秒懂springboot druid 配置

    一文秒懂springboot druid 配置

    Druid是阿里巴巴開發(fā)的一個連接池,他提供了一個高效、功能強大、可擴展性好的數(shù)據(jù)庫連接池,區(qū)別于hikari,今天通過本文給大家分享springboot druid 配置教程,需要的朋友參考下吧
    2021-08-08
  • Java?處理樹形結(jié)構(gòu)數(shù)據(jù)的過程

    Java?處理樹形結(jié)構(gòu)數(shù)據(jù)的過程

    這篇文章主要介紹了Java?處理樹形結(jié)構(gòu)數(shù)據(jù)的過程,本文給大家分析具體實現(xiàn)過程,結(jié)合實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-08-08
  • springboot中thymeleaf模板使用詳解

    springboot中thymeleaf模板使用詳解

    這篇文章將更加全面詳細的介紹thymeleaf的使用。thymeleaf 是新一代的模板引擎,在spring4.0中推薦使用thymeleaf來做前端模版引擎。
    2017-05-05
  • 詳解maven的install的作用

    詳解maven的install的作用

    這篇文章主要介紹了詳解maven的install的作用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Spring基于xml實現(xiàn)自動裝配流程詳解

    Spring基于xml實現(xiàn)自動裝配流程詳解

    自動裝配是使用spring滿足bean依賴的一種方法,spring會在應(yīng)用上下文中為某個bean尋找其依賴的bean,Spring中bean有三種裝配機制,分別是:在xml中顯式配置、在java中顯式配置、隱式的bean發(fā)現(xiàn)機制和自動裝配
    2023-01-01
  • SpringMVC請求的路徑變量里面寫正則表達式的方法

    SpringMVC請求的路徑變量里面寫正則表達式的方法

    這篇文章主要介紹了SpringMVC請求的路徑變量里面寫正則表達式的相關(guān)知識,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-09-09

最新評論