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

SpringBoot實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換的方法總結(jié)

 更新時(shí)間:2023年12月25日 15:04:09   作者:程序員小富  
項(xiàng)目開發(fā)中經(jīng)常會(huì)遇到多數(shù)據(jù)源同時(shí)使用的場(chǎng)景,比如冷熱數(shù)據(jù)的查詢等情況,所以接下來(lái)本文就來(lái)介紹一下如何使用實(shí)現(xiàn)自定義注解的形式來(lái)實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換吧

簡(jiǎn)介

項(xiàng)目開發(fā)中經(jīng)常會(huì)遇到多數(shù)據(jù)源同時(shí)使用的場(chǎng)景,比如冷熱數(shù)據(jù)的查詢等情況,我們可以使用類似現(xiàn)成的工具包來(lái)解決問(wèn)題,但在多數(shù)據(jù)源的使用中通常伴隨著定制化的業(yè)務(wù),所以一般的公司還是會(huì)自行實(shí)現(xiàn)多數(shù)據(jù)源切換的功能,接下來(lái)一起使用實(shí)現(xiàn)自定義注解的形式來(lái)實(shí)現(xiàn)一下。

基礎(chǔ)配置

yml配置

pom.xml文件引入必要的Jar

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.6</version>
    </parent>
    <groupId>com.dynamic</groupId>
    <artifactId>springboot-dynamic-datasource</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <mybatis.plus.version>3.5.3.1</mybatis.plus.version>
        <mysql.connector.version>8.0.32</mysql.connector.version>
        <druid.version>1.2.6</druid.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- springboot核心包 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mysql驅(qū)動(dòng)包 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>${mysql.connector.version}</version>
        </dependency>
        <!-- lombok工具包 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- MyBatis Plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis.plus.version}</version>
        </dependency>
        <!-- druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.7</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

管理數(shù)據(jù)源

我們應(yīng)用ThreadLocal來(lái)管理數(shù)據(jù)源信息,通過(guò)其中內(nèi)容的get,set,remove方法來(lái)獲取、設(shè)置、刪除當(dāng)前線程對(duì)應(yīng)的數(shù)據(jù)源。

/**
 * ThreadLocal存放數(shù)據(jù)源變量
 *
 * @author 公眾號(hào):程序員小富
 * @date 2023/11/27 11:02
 */
public class DataSourceContextHolder {

    private static final ThreadLocal<String> DATASOURCE_HOLDER = new ThreadLocal<>();

    /**
     * 獲取當(dāng)前線程的數(shù)據(jù)源
     *
     * @return 數(shù)據(jù)源名稱
     */
    public static String getDataSource() {
        return DATASOURCE_HOLDER.get();
    }

    /**
     * 設(shè)置數(shù)據(jù)源
     *
     * @param dataSourceName 數(shù)據(jù)源名稱
     */
    public static void setDataSource(String dataSourceName) {
        DATASOURCE_HOLDER.set(dataSourceName);
    }

    /**
     * 刪除當(dāng)前數(shù)據(jù)源
     */
    public static void removeDataSource() {
        DATASOURCE_HOLDER.remove();
    }
}

重置數(shù)據(jù)源

創(chuàng)建 DynamicDataSource 類并繼承 AbstractRoutingDataSource,這樣我們就可以重置當(dāng)前的數(shù)據(jù)庫(kù)路由,實(shí)現(xiàn)切換成想要執(zhí)行的目標(biāo)數(shù)據(jù)庫(kù)。

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;

/**
 * 重置當(dāng)前的數(shù)據(jù)庫(kù)路由,實(shí)現(xiàn)切換成想要執(zhí)行的目標(biāo)數(shù)據(jù)庫(kù)
 *
 * @author 公眾號(hào):程序員小富
 * @date 2023/11/27 11:02
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    public DynamicDataSource(DataSource defaultDataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultDataSource);
        super.setTargetDataSources(targetDataSources);
    }

    /**
     * 這一步是關(guān)鍵,獲取注冊(cè)的數(shù)據(jù)源信息
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }
}

配置數(shù)據(jù)庫(kù)

在 application.yml 中配置數(shù)據(jù)庫(kù)信息,使用dynamic_datasource_1、dynamic_datasource_2兩個(gè)數(shù)據(jù)庫(kù)

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      master:
        url: jdbc:mysql://127.0.0.1:3306/dynamic_datasource_1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
        username: root
        password: 12345
        driver-class-name: com.mysql.cj.jdbc.Driver
      slave:
        url: jdbc:mysql://127.0.0.1:3306/dynamic_datasource_2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
        username: root
        password: 12345
        driver-class-name: com.mysql.cj.jdbc.Driver

再將多個(gè)數(shù)據(jù)源注冊(cè)到DataSource.

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
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;

/**
 * 注冊(cè)多個(gè)數(shù)據(jù)源
 *
 * @author 公眾號(hào):程序員小富
 * @date 2023/11/27 11:02
 */
@Configuration
public class DateSourceConfig {

    @Bean
    @ConfigurationProperties("spring.datasource.druid.master")
    public DataSource dynamicDatasourceMaster() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.druid.slave")
    public DataSource dynamicDatasourceSlave() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource createDynamicDataSource() {
        Map<Object, Object> dataSourceMap = new HashMap<>();
        // 設(shè)置默認(rèn)的數(shù)據(jù)源為Master
        DataSource defaultDataSource = dynamicDatasourceMaster();
        dataSourceMap.put("master", defaultDataSource);
        dataSourceMap.put("slave", dynamicDatasourceSlave());
        return new DynamicDataSource(defaultDataSource, dataSourceMap);
    }
}

啟動(dòng)類配置

在啟動(dòng)類的@SpringBootApplication注解中排除DataSourceAutoConfiguration,否則會(huì)報(bào)錯(cuò)。

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

到這多數(shù)據(jù)源的基礎(chǔ)配置就結(jié)束了,接下來(lái)測(cè)試一下

測(cè)試切換

準(zhǔn)備SQL

創(chuàng)建兩個(gè)庫(kù)dynamic_datasource_1、dynamic_datasource_2,庫(kù)中均創(chuàng)建同一張表 t_dynamic_datasource_data。

CREATE TABLE `t_dynamic_datasource_data` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `source_name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
);

dynamic_datasource_1.t_dynamic_datasource_data表中插入

insert into t_dynamic_datasource_data (source_name) value ('dynamic_datasource_master');

dynamic_datasource_2.t_dynamic_datasource_data表中插入

insert into t_dynamic_datasource_data (source_name) value ('dynamic_datasource_slave');

手動(dòng)切換數(shù)據(jù)源

這里我準(zhǔn)備了一個(gè)接口來(lái)驗(yàn)證,傳入的 datasourceName 參數(shù)值就是剛剛注冊(cè)的數(shù)據(jù)源的key。

/**
 * 動(dòng)態(tài)數(shù)據(jù)源切換
 *
 * @author 公眾號(hào):程序員小富
 * @date 2023/11/27 11:02
 */
@RestController
public class DynamicSwitchController {

    @Resource
    private DynamicDatasourceDataMapper dynamicDatasourceDataMapper;

    @GetMapping("/switchDataSource/{datasourceName}")
    public String switchDataSource(@PathVariable("datasourceName") String datasourceName) {
        DataSourceContextHolder.setDataSource(datasourceName);
        DynamicDatasourceData dynamicDatasourceData = dynamicDatasourceDataMapper.selectOne(null);
        DataSourceContextHolder.removeDataSource();
        return dynamicDatasourceData.getSourceName();
    }
}

傳入?yún)?shù)master時(shí):127.0.0.1:9004/switchDataSource/master

傳入?yún)?shù)slave時(shí):127.0.0.1:9004/switchDataSource/slave

通過(guò)執(zhí)行結(jié)果,我們看到傳遞不同的數(shù)據(jù)源名稱,已經(jīng)實(shí)現(xiàn)了查詢對(duì)應(yīng)的數(shù)據(jù)庫(kù)數(shù)據(jù)。

注解切換數(shù)據(jù)源

上邊已經(jīng)成功實(shí)現(xiàn)了手動(dòng)切換數(shù)據(jù)源,但這種方式頂多算是半自動(dòng),下邊我們來(lái)使用注解方式實(shí)現(xiàn)動(dòng)態(tài)切換。

定義注解

我們先定一個(gè)名為DS的注解,作用域?yàn)镸ETHOD方法上,由于@DS中設(shè)置的默認(rèn)值是:master,因此在調(diào)用主數(shù)據(jù)源時(shí),可以不用進(jìn)行傳值。

/**
 * 定于數(shù)據(jù)源切換注解
 *
 * @author 公眾號(hào):程序員小富
 * @date 2023/11/27 11:02
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DS {
    // 默認(rèn)數(shù)據(jù)源master
    String value() default "master";
}

實(shí)現(xiàn)AOP

定義了@DS注解后,緊接著實(shí)現(xiàn)注解的AOP邏輯,拿到注解傳遞值,然后設(shè)置當(dāng)前線程的數(shù)據(jù)源

import com.dynamic.config.DataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
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.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * 實(shí)現(xiàn)@DS注解的AOP切面
 *
 * @author 公眾號(hào):程序員小富
 * @date 2023/11/27 11:02
 */
@Aspect
@Component
@Slf4j
public class DSAspect {

    @Pointcut("@annotation(com.dynamic.aspect.DS)")
    public void dynamicDataSource() {
    }

    @Around("dynamicDataSource()")
    public Object datasourceAround(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DS ds = method.getAnnotation(DS.class);
        if (Objects.nonNull(ds)) {
            DataSourceContextHolder.setDataSource(ds.value());
        }
        try {
            return point.proceed();
        } finally {
            DataSourceContextHolder.removeDataSource();
        }
    }
}

測(cè)試注解

再添加兩個(gè)接口測(cè)試,使用@DS注解標(biāo)注,使用不同的數(shù)據(jù)源名稱,內(nèi)部執(zhí)行相同的查詢條件,看看結(jié)果如何?

@DS(value = "master")
@GetMapping("/dbMaster")
public String dbMaster() {
    DynamicDatasourceData dynamicDatasourceData = dynamicDatasourceDataMapper.selectOne(null);
    return dynamicDatasourceData.getSourceName();
}

@DS(value = "slave")
@GetMapping("/dbSlave")
public String dbSlave() {
    DynamicDatasourceData dynamicDatasourceData = dynamicDatasourceDataMapper.selectOne(null);
    return dynamicDatasourceData.getSourceName();
}

通過(guò)執(zhí)行結(jié)果,看到通過(guò)應(yīng)用@DS注解也成功的進(jìn)行了數(shù)據(jù)源的切換。

事務(wù)管理

在動(dòng)態(tài)切換數(shù)據(jù)源的時(shí)候有一個(gè)問(wèn)題是要考慮的,那就是事務(wù)管理是否還會(huì)生效呢?

我們做個(gè)測(cè)試,新增一個(gè)接口分別插入兩條記錄,其中在插入第二條數(shù)據(jù)時(shí)將值設(shè)置超過(guò)了字段長(zhǎng)度限制,會(huì)產(chǎn)生Data too long for column異常。

    /**
     * 驗(yàn)證一下事物控制
     */
//    @Transactional(rollbackFor = Exception.class)
    @DS(value = "slave")
    @GetMapping("/dbTestTransactional")
    public void dbTestTransactional() {

        DynamicDatasourceData datasourceData = new DynamicDatasourceData();
        datasourceData.setSourceName("test");
        dynamicDatasourceDataMapper.insert(datasourceData);

        DynamicDatasourceData datasourceData1 = new DynamicDatasourceData();
        datasourceData1.setSourceName("testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest");
        dynamicDatasourceDataMapper.insert(datasourceData1);
    }

經(jīng)過(guò)測(cè)試發(fā)現(xiàn)執(zhí)行結(jié)果如下,即便實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源,本地事務(wù)依然可以生效。

  • 不加上@Transactional注解第一條記錄可以插入,第二條插入失敗
  • 加上@Transactional注解兩條記錄都不會(huì)插入成功

本文案例地址:

以上就是SpringBoot實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換的方法總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot動(dòng)態(tài)數(shù)據(jù)源切換的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • mybatis postgresql 批量刪除操作方法

    mybatis postgresql 批量刪除操作方法

    PostgreSQL是一種特性非常齊全的自由軟件的對(duì)象-關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng)(ORDBMS),這篇文章主要介紹了mybatis postgresql 批量刪除操作,需要的朋友可以參考下
    2020-02-02
  • spring boot項(xiàng)目同時(shí)傳遞參數(shù)和文件的多種方式代碼演示

    spring boot項(xiàng)目同時(shí)傳遞參數(shù)和文件的多種方式代碼演示

    這篇文章主要介紹了spring boot項(xiàng)目同時(shí)傳遞參數(shù)和文件的多種方式,在開發(fā)接口中,遇到了需要同時(shí)接收參數(shù)和文件的情況,可以有多種方式實(shí)現(xiàn)文件+參數(shù)的接收,這里基于spring boot 3 + vue 3 + axios,做一個(gè)簡(jiǎn)單的代碼演示,需要的朋友可以參考下
    2023-06-06
  • Java畢業(yè)設(shè)計(jì)實(shí)戰(zhàn)之健身器材商城系統(tǒng)的實(shí)現(xiàn)

    Java畢業(yè)設(shè)計(jì)實(shí)戰(zhàn)之健身器材商城系統(tǒng)的實(shí)現(xiàn)

    只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+Jdbc+Servlet+Ajax+Fileupload+mysql實(shí)現(xiàn)健身器材商城系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平
    2022-03-03
  • Springboot+devtools配置熱部署過(guò)程

    Springboot+devtools配置熱部署過(guò)程

    SpringBoot提供的spring-boot-devtools模塊可以實(shí)現(xiàn)應(yīng)用的熱部署,極大提升開發(fā)效率,通過(guò)添加POM依賴、配置yml文件、使用IDE快捷鍵等步驟,可以無(wú)需手動(dòng)重啟即可自動(dòng)加載變更,這種方式不僅適用于靜態(tài)文件的自動(dòng)加載,也適用于整個(gè)SpringBoot工程,是提高開發(fā)效率的有效手段
    2024-11-11
  • Java 全方位講解面向?qū)ο筇攸c(diǎn)與使用

    Java 全方位講解面向?qū)ο筇攸c(diǎn)與使用

    面向?qū)ο箝_發(fā)方法將面向?qū)ο蟮乃枷霊?yīng)用于軟件開發(fā)過(guò)程中,指導(dǎo)開發(fā)活動(dòng),是建立在“對(duì)象”概念基礎(chǔ)上的方法學(xué),簡(jiǎn)稱OO( Object-Oriented)方法
    2022-04-04
  • Java實(shí)現(xiàn)List轉(zhuǎn)換為Map的方法小結(jié)

    Java實(shí)現(xiàn)List轉(zhuǎn)換為Map的方法小結(jié)

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)List轉(zhuǎn)換為Map的一些常見的方法,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,有需要的小伙伴可以參考一下
    2024-03-03
  • mybatis trim標(biāo)簽的使用詳解

    mybatis trim標(biāo)簽的使用詳解

    這篇文章主要介紹了mybatis trim標(biāo)簽的使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • Java中的String.valueOf()和toString()方法區(qū)別小結(jié)

    Java中的String.valueOf()和toString()方法區(qū)別小結(jié)

    字符串操作是開發(fā)者日常編程任務(wù)中不可或缺的一部分,轉(zhuǎn)換為字符串是一種常見需求,其中最常見的就是String.valueOf()和toString()方法,本文主要介紹了Java中的String.valueOf()和toString()方法區(qū)別小結(jié),感興趣的可以了解一下
    2025-04-04
  • Java多態(tài)的使用注意事項(xiàng)

    Java多態(tài)的使用注意事項(xiàng)

    本文講解了什么是JAVA多態(tài)和Java多態(tài)是如何實(shí)現(xiàn)的,在使用Java多態(tài)時(shí)需要注意什么,具體大家看下面的內(nèi)容
    2013-11-11
  • Java中對(duì)象的銷毀方法分析

    Java中對(duì)象的銷毀方法分析

    這篇文章主要介紹了Java中對(duì)象的銷毀方法,較為詳細(xì)的分析了對(duì)象的功能、用法及銷毀對(duì)象對(duì)于程序運(yùn)行的益處,需要的朋友可以參考下
    2015-04-04

最新評(píng)論

临清市| 手游| 武清区| 资阳市| 蕲春县| 安丘市| 阳新县| 获嘉县| 汪清县| 元阳县| 武义县| 积石山| 来凤县| 二手房| 百色市| 页游| 济南市| 灵石县| 遵义县| 北票市| 澄城县| 杭州市| 都匀市| 历史| 泗阳县| 金溪县| 哈巴河县| 浦县| 扶风县| 饶阳县| 汝南县| 什邡市| 广南县| 环江| 甘洛县| 吴忠市| 抚远县| 贵阳市| 彭阳县| 乌兰县| 西丰县|