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

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

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

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

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

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

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

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

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

1. 添加依賴 (pom.xml)

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

<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> <!-- 請(qǐng)使用較新版本 -->
    </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ù)源配置類

為每個(gè)數(shù)據(jù)源創(chuàng)建一個(gè) 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 庫(kù)的 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 庫(kù)的 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 中直接注入并使用對(duì)應(yīng)的 Mapper,Spring 會(huì)自動(dòng)為它們關(guān)聯(lián)正確的數(shù)據(jù)源。

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

    @Autowired
    private OrderSlaveMapper orderSlaveMapper; // 操作 slave 庫(kù)

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

優(yōu)點(diǎn):配置隔離,結(jié)構(gòu)非常清晰,不會(huì)混淆。

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

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

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

1. 配置文件 (application.yml)

與方案一相同。

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

創(chuàng)建一個(gè)注解,用于標(biāo)記方法應(yīng)該使用哪個(gè)數(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 來存儲(chǔ)當(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)建動(dò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)建一個(gè)切面,攔截 @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)建一個(gè)統(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 // 必須!將動(dòng)態(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 只需要配置一個(gè),并讓它們使用這個(gè) @PrimaryDynamicDataSource 即可。Spring Boot 會(huì)自動(dòng)配置好。

7. 使用

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

@Service
public class ProductService {

    @Autowired
    private ProductMapper productMapper;

    // 默認(rèn)不加注解,使用 master 數(shù)據(jù)源(因?yàn)槲覀兣渲昧四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 綁定的是同一個(gè) DataSource,@Transactional 注解就能正常工作。在動(dòng)態(tài)方案中,事務(wù)管理器綁定的是 DynamicDataSource,它能確保事務(wù)在當(dāng)前線程選擇的數(shù)據(jù)源上生效。
  • 跨數(shù)據(jù)源事務(wù)(分布式事務(wù)):標(biāo)準(zhǔn)的 @Transactional 無法管理跨多個(gè)數(shù)據(jù)源的事務(wù)。如果你需要在同一個(gè)方法中對(duì) masterslave 都進(jìn)行寫操作,并保證它們的原子性,你需要引入 JTA(Java Transaction API)事務(wù)管理器,例如 AtomikosNarayana。這會(huì)增加配置的復(fù)雜度。

總結(jié)

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

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

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

相關(guān)文章

最新評(píng)論

沾化县| 华亭县| 汝城县| 迁西县| 车致| 永嘉县| 博湖县| 西贡区| 长子县| 富宁县| 清原| 右玉县| 常熟市| 信丰县| 来凤县| 桃园市| 红桥区| 和顺县| 远安县| 缙云县| 高安市| 泌阳县| 宿松县| 中西区| 碌曲县| 綦江县| 河北区| 郎溪县| 内丘县| 当阳市| 临清市| 万山特区| 辉县市| 萨迦县| 界首市| 灵台县| 贵州省| 苏州市| 鹤壁市| 龙泉市| 杭州市|