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

SpringBoot項(xiàng)目中如何動態(tài)切換數(shù)據(jù)源、數(shù)據(jù)庫

 更新時間:2025年02月10日 10:09:35   作者:小杰不禿頭  
本文主要介紹了SpringBoot項(xiàng)目中如何動態(tài)切換數(shù)據(jù)源、數(shù)據(jù)庫,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

本文參考若依源碼,介紹了如何在SpringBoot項(xiàng)目中使用AOP和自定義注解實(shí)現(xiàn)MySQL主從數(shù)據(jù)庫的動態(tài)切換,當(dāng)從庫故障時,能自動切換到主庫,確保服務(wù)的高可用性。

實(shí)現(xiàn)效果:如果 服務(wù)器搭建的是一主多從多個mysql數(shù)據(jù)源,主服務(wù)器用來讀。從服務(wù)器用來寫。此時你在代碼層面用注解指定了一個增刪改方法到從數(shù)據(jù)源,但是碰巧此時從數(shù)據(jù)源失效了,那么就會自動的切換到其它服務(wù)器。

為什么要切換數(shù)據(jù)源,有哪些應(yīng)用場景?

動態(tài)切換數(shù)據(jù)源通常是為了滿足以下需求:

  • 讀寫分離:在數(shù)據(jù)庫架構(gòu)中,為了提高性能和可用性,常常使用主從復(fù)制的方式。主數(shù)據(jù)庫處理寫操作,而從數(shù)據(jù)庫處理讀操作。動態(tài)切換數(shù)據(jù)源可以在不同的操作中使用不同的數(shù)據(jù)庫,以達(dá)到優(yōu)化性能的目的。
  • 多租戶架構(gòu):在SaaS(Software as a Service)應(yīng)用中,不同的租戶可能需要操作不同的數(shù)據(jù)庫。動態(tài)數(shù)據(jù)源允許系統(tǒng)根據(jù)租戶的身份來切換到對應(yīng)的數(shù)據(jù)源。
  • 分庫分表:在處理大規(guī)模數(shù)據(jù)時,可能會采用分庫分表的策略來分散數(shù)據(jù)存儲的壓力。動態(tài)切換數(shù)據(jù)源可以在執(zhí)行跨庫或跨表操作時,根據(jù)需要切換到正確的數(shù)據(jù)源。
  • 環(huán)境隔離:在開發(fā)、測試和生產(chǎn)環(huán)境中,可能需要連接到不同的數(shù)據(jù)庫。動態(tài)數(shù)據(jù)源可以在不同環(huán)境之間無縫切換,以確保數(shù)據(jù)的隔離和安全性。
  • 靈活的數(shù)據(jù)庫管理:在復(fù)雜的業(yè)務(wù)場景下,可能需要根據(jù)不同的業(yè)務(wù)邏輯來選擇不同的數(shù)據(jù)源。動態(tài)數(shù)據(jù)源提供了這種靈活性,允許開發(fā)者根據(jù)運(yùn)行時的條件來選擇最合適的數(shù)據(jù)源。
  • 故障轉(zhuǎn)移和高可用性:當(dāng)主數(shù)據(jù)庫不可用時,動態(tài)切換數(shù)據(jù)源可以自動或手動切換到備用數(shù)據(jù)庫,以保證服務(wù)的連續(xù)性和數(shù)據(jù)的可用性。

如何切換數(shù)據(jù)源?

SpringBoot版本:3.0.4

jdk版本:JDK17

1.pom文件

<dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>
    <!-- aop切面-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <!--druid連接池-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid-spring-boot-starter</artifactId>
      <version>1.2.20</version>
    </dependency>
    <!--mysql驅(qū)動-->
    <dependency>
      <groupId>com.mysql</groupId>
      <artifactId>mysql-connector-j</artifactId>
    </dependency>
    <!--MybatisPlus-->
    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-boot-starter</artifactId>
      <version>3.5.3.1</version>
    </dependency>

2.配置文件:application.yml、application-druid.yml

application.yml配置文件:

#application.yml

server:
  port: 8000
spring:
  profiles:
    active: druid

 application-druid.yml配置文件:

# 數(shù)據(jù)源配置
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    druid:
      # 主庫數(shù)據(jù)源
      master:
        url: jdbc:mysql://localhost:3306/study?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: 123456
      # 從庫數(shù)據(jù)源
      slave:
        # 從數(shù)據(jù)源開關(guān)/默認(rèn)關(guān)閉
        enabled: true
        url: jdbc:mysql://localhost:3306/t_lyj?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: 123456
      # 初始連接數(shù)
      initialSize: 5
      # 最小連接池數(shù)量
      minIdle: 10
      # 最大連接池數(shù)量
      maxActive: 20
      # 配置獲取連接等待超時的時間
      maxWait: 60000
      # 配置連接超時時間
      connectTimeout: 30000
      # 配置網(wǎng)絡(luò)超時時間
      socketTimeout: 60000
      # 配置間隔多久才進(jìn)行一次檢測,檢測需要關(guān)閉的空閑連接,單位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置一個連接在池中最小生存的時間,單位是毫秒
      minEvictableIdleTimeMillis: 300000
      # 配置一個連接在池中最大生存的時間,單位是毫秒
      maxEvictableIdleTimeMillis: 900000

3、數(shù)據(jù)源名稱枚舉DataSourceType

/**
 * 數(shù)據(jù)源
 * 
 * @author ruoyi
 */
public enum DataSourceType
{
    /**
     * 主庫
     */
    MASTER,

    /**
     * 從庫
     */
    SLAVE
}

4、Bean工具類SpringUtils

@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware 
{
    /** Spring應(yīng)用上下文環(huán)境 */
    private static ConfigurableListableBeanFactory beanFactory;

    private static ApplicationContext applicationContext;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException 
    {
        SpringUtils.beanFactory = beanFactory;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException 
    {
        SpringUtils.applicationContext = applicationContext;
    }

    /**
     * 獲取對象
     *
     * @param name
     * @return Object 一個以所給名字注冊的bean的實(shí)例
     * @throws BeansException
     *
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException
    {
        return (T) beanFactory.getBean(name);
    }

    /**
     * 獲取類型為requiredType的對象
     *
     * @param clz
     * @return
     * @throws BeansException
     *
     */
    public static <T> T getBean(Class<T> clz) throws BeansException
    {
        T result = (T) beanFactory.getBean(clz);
        return result;
    }

    /**
     * 如果BeanFactory包含一個與所給名稱匹配的bean定義,則返回true
     *
     * @param name
     * @return boolean
     */
    public static boolean containsBean(String name)
    {
        return beanFactory.containsBean(name);
    }

    /**
     * 判斷以給定名字注冊的bean定義是一個singleton還是一個prototype。 如果與給定名字相應(yīng)的bean定義沒有被找到,將會拋出一個異常(NoSuchBeanDefinitionException)
     *
     * @param name
     * @return boolean
     * @throws NoSuchBeanDefinitionException
     *
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.isSingleton(name);
    }

    /**
     * @param name
     * @return Class 注冊對象的類型
     * @throws NoSuchBeanDefinitionException
     *
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.getType(name);
    }

    /**
     * 如果給定的bean名字在bean定義中有別名,則返回這些別名
     *
     * @param name
     * @return
     * @throws NoSuchBeanDefinitionException
     *
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.getAliases(name);
    }

    /**
     * 獲取aop代理對象
     * 
     * @param invoker
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAopProxy(T invoker)
    {
        return (T) AopContext.currentProxy();
    }

    /**
     * 獲取當(dāng)前的環(huán)境配置,無配置返回null
     *
     * @return 當(dāng)前的環(huán)境配置
     */
    public static String[] getActiveProfiles()
    {
        return applicationContext.getEnvironment().getActiveProfiles();
    }

    /**
     * 獲取當(dāng)前的環(huán)境配置,當(dāng)有多個環(huán)境配置時,只獲取第一個
     *
     * @return 當(dāng)前的環(huán)境配置
     */
    public static String getActiveProfile()
    {
        final String[] activeProfiles = getActiveProfiles();
        return StringUtils.isNotEmpty(Arrays.toString(activeProfiles)) ? activeProfiles[0] : null;
    }

    /**
     * 獲取配置文件中的值
     *
     * @param key 配置文件的key
     * @return 當(dāng)前的配置文件的值
     *
     */
    public static String getRequiredProperty(String key)
    {
        return applicationContext.getEnvironment().getRequiredProperty(key);
    }
}

5、多數(shù)據(jù)源切換注解DataSource

/**
 * 自定義多數(shù)據(jù)源切換注解
 *
 * 優(yōu)先級:先方法,后類,如果方法覆蓋了類上的數(shù)據(jù)源類型,以方法的為準(zhǔn),否則以類上的為準(zhǔn)
 *
 * @author lyj
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource
{
    /**
     * 切換數(shù)據(jù)源名稱
     */
    public DataSourceType value() default DataSourceType.MASTER;
}

6、數(shù)據(jù)源解析配置類DruidConfig

@Configuration
public class DruidConfig {

    @Bean
    @ConfigurationProperties("spring.datasource.druid.master")
    public DataSource masterDataSource(DruidProperties druidProperties){
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }

    @Bean
    @ConfigurationProperties("spring.datasource.druid.slave")
    @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
    public DataSource slaveDataSource(DruidProperties druidProperties) {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }

    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource(DataSource masterDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
        setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
        return new DynamicDataSource(masterDataSource, targetDataSources);
    }


    /**
     * 設(shè)置數(shù)據(jù)源
     *
     * @param targetDataSources 備選數(shù)據(jù)源集合
     * @param sourceName 數(shù)據(jù)源名稱
     * @param beanName bean名稱
     */
    public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName) {
        try {
            DataSource dataSource = SpringUtils.getBean(beanName);
            targetDataSources.put(sourceName, dataSource);
        } catch (Exception e) {
        }
    }
}

7、數(shù)據(jù)源注入核心類DynamicDataSource

/**
 * 動態(tài)數(shù)據(jù)源
 * 
 * @author lyj
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
    {
        //設(shè)置默認(rèn)數(shù)據(jù)源
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        //設(shè)置目標(biāo)數(shù)據(jù)源的映射
        super.setTargetDataSources(targetDataSources);
        //初始化
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey()
    {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

8、數(shù)據(jù)源切換處理類DynamicDataSourceContextHolder

/**
 * 數(shù)據(jù)源切換處理
 * 
 * @author lyj
 */
public class DynamicDataSourceContextHolder
{
    public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);

    /**
     * 使用ThreadLocal維護(hù)變量,ThreadLocal為每個使用該變量的線程提供獨(dú)立的變量副本,
     * 所以每一個線程都可以獨(dú)立地改變自己的副本,而不會影響其它線程所對應(yīng)的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 設(shè)置數(shù)據(jù)源的變量
     */
    public static void setDataSourceType(String dsType)
    {
        log.info("切換到{}數(shù)據(jù)源", dsType);
        CONTEXT_HOLDER.set(dsType);
    }

    /**
     * 獲得數(shù)據(jù)源的變量,默認(rèn)使用主數(shù)據(jù)源
     */
    public static String getDataSourceType()
    {
        return CONTEXT_HOLDER.get() == null ? DataSourceType.MASTER.name() : CONTEXT_HOLDER.get();
    }

    /**
     * 清空數(shù)據(jù)源變量
     */
    public static void clearDataSourceType()
    {
        CONTEXT_HOLDER.remove();
    }
}

9、Aop切面類

@Aspect
@Order(1)
@Component
public class DataSourceAspect {


    @Pointcut("@annotation(com.LYJ.study.DynamicDataSource.annocation.DataSource)"
            + "|| @within(com.LYJ.study.DynamicDataSource.annocation.DataSource)")
    public void dsPointCut(){}

    @Around("dsPointCut()")

    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        DataSource dataSource = getDataSource(joinPoint);
        if (dataSource != null){
            DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
        }
        try {
            return joinPoint.proceed();
        }
        finally {
            // 銷毀數(shù)據(jù)源 在執(zhí)行方法之后
            DynamicDataSourceContextHolder.clearDataSourceType();
        }
    }

    /**
     * 獲取需要切換的數(shù)據(jù)源
     */
    public DataSource getDataSource(ProceedingJoinPoint point)
    {
        MethodSignature signature = (MethodSignature) point.getSignature();
        com.LYJ.study.DynamicDataSource.annocation.DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), com.LYJ.study.DynamicDataSource.annocation.DataSource.class);
        if (Objects.nonNull(dataSource)) {
            return dataSource;
        }
        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
    }
}

10、在業(yè)務(wù)中使用

047da38f2fbf4c12bd08862b9eb125cb.png

@Service
@RequiredArgsConstructor
@DataSource(value=DataSourceType.MASTER)
//@DataSource(value=DataSourceType.SLAVE)
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final UserMapper userMapper;
    @Override
    @DataSource(value=DataSourceType.MASTER)
    //@DataSource(value=DataSourceType.SLAVE)
    public List<User> queryAll() {
        return userMapper.selectList(null);
    }
}

我們在service、mapper的類和方法上使用都可以。 

補(bǔ)充:有很多從數(shù)據(jù)源怎么辦? 

我們上面已經(jīng)配置了一個從數(shù)據(jù)源了,接下來我們繼續(xù)配置多個從數(shù)據(jù)源

首先在application-druid.yml文件添加新的數(shù)據(jù)源

4f889c09f9854a06bdfb41bf03773d3f.png

在枚舉添加數(shù)據(jù)源名稱

//如果配置多數(shù)據(jù)源,繼續(xù)添加即可
public enum DataSourceType
{
    /**
     * 主庫
     */
    MASTER,

    /**
     * 從庫
     */
    SLAVE,
    /**
     * 從庫2
     */
    SLAVE2
}

4a6c1e4cb24d44e6ba4a167b377cc2b0.png

如何切換數(shù)據(jù)庫?

我們就以O(shè)racle為例

 <!--oracle驅(qū)動-->
    <dependency>
      <groupId>com.oracle</groupId>
      <artifactId>ojdbc6</artifactId>
      <version>11.2.0.3</version>
    </dependency>

在application-druid.yml添加

      slave3:
        # 從數(shù)據(jù)源開關(guān)/默認(rèn)關(guān)閉
        enabled: true
        url: jdbc:oracle:thin:@127.0.0.1:1521:oracle
        username: root
        password: password

 然后刪除指定的mysql驅(qū)動,默認(rèn)會自動尋找驅(qū)動

7b37f9d30cbe4cdfb4c61b174a47fb13.png

添加數(shù)據(jù)源和用法參考上面即可,都是一樣的。 

注意:在切換數(shù)據(jù)庫時,因?yàn)閙ysql跟Oracle的sql語法有差別,啟動時可能報錯。

到此這篇關(guān)于SpringBoot項(xiàng)目中如何動態(tài)切換數(shù)據(jù)源、數(shù)據(jù)庫的文章就介紹到這了,更多相關(guān)SpringBoot動態(tài)切換數(shù)據(jù)源內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • SpringBoot3應(yīng)用中集成和使用Spring Retry的實(shí)踐記錄

    SpringBoot3應(yīng)用中集成和使用Spring Retry的實(shí)踐記錄

    SpringRetry為SpringBoot3提供重試機(jī)制,支持注解和編程式兩種方式,可配置重試策略與監(jiān)聽器,適用于臨時性故障場景,需合理設(shè)置次數(shù)、退避策略并做好監(jiān)控,本文給大家詳細(xì)介紹如何在 SpringBoot 3 應(yīng)用中集成和使用 Spring Retry,感興趣的朋友一起看看吧
    2025-06-06
  • IDEA緩存配置等位置更改過程

    IDEA緩存配置等位置更改過程

    本文介紹了將IDEA移動到D盤的具體步驟,包括找到原存儲目錄、創(chuàng)建目標(biāo)存儲目錄、關(guān)閉IDEA和JetBrainsToolbox、移動文件、刪除原目錄以及創(chuàng)建符號鏈接等步驟
    2026-04-04
  • jvm分析工具/適用場景/最佳實(shí)踐過程

    jvm分析工具/適用場景/最佳實(shí)踐過程

    本文系統(tǒng)介紹JVM分析工具的分類與適用場景,涵蓋CPU監(jiān)控、內(nèi)存泄漏分析、FullGC排查、線程死鎖檢測及方法級性能優(yōu)化,提供工具選擇、最佳實(shí)踐和避坑指南,助力高效解決Java應(yīng)用性能與穩(wěn)定性問題
    2025-07-07
  • Springboot項(xiàng)目中使用EasyPOI操作Excel的詳細(xì)教程

    Springboot項(xiàng)目中使用EasyPOI操作Excel的詳細(xì)教程

    EasyPOI簡介:EasyPOI是一款用于簡化POI開發(fā)的類庫,它提供了Excel、CSV、Word的快速導(dǎo)入導(dǎo)出功能,文章詳細(xì)介紹了如何使用EasyPOI進(jìn)行注解方式導(dǎo)出和導(dǎo)入Excel,以及模板方式導(dǎo)出Excel,同時,還簡要介紹了CSV的導(dǎo)出方式
    2026-01-01
  • IDEA添加getter、setter方法的快捷鍵:Alt+Insert方式

    IDEA添加getter、setter方法的快捷鍵:Alt+Insert方式

    本文詳細(xì)介紹了在IDEA中使用Alt+Insert快捷鍵時遇到的問題的解決方法,包括檢查鍵盤布局、設(shè)置Keymap以及調(diào)整Intention選項(xiàng)等幫助用戶順利使用該快捷鍵
    2026-05-05
  • java 字浮串提取方法匯集

    java 字浮串提取方法匯集

    用方法toCharArray public char[] toCharArray()
    2008-10-10
  • SpringBoot對接Spark過程詳解

    SpringBoot對接Spark過程詳解

    這篇文章主要介紹SpringBoot接入Spark的方法的相關(guān)知識,小編通過實(shí)際案例向大家展示操作過程,操作方法簡單快捷,實(shí)用性強(qiáng),希望能幫助大家解決問題
    2023-02-02
  • Java中的OpenJDK使用原理

    Java中的OpenJDK使用原理

    這篇文章主要介紹了Java中的OpenJDK使用原理,OpenJDK是Java的開發(fā)工具包,關(guān)于Java為什么要使用它文章作簡單介紹,感興趣的朋友可以參考一下
    2022-06-06
  • 一個Java工程師超實(shí)用的17個日常效率工具

    一個Java工程師超實(shí)用的17個日常效率工具

    這篇文章主要總結(jié)介紹了一個Java工程師超實(shí)用的17個日常效率工具,中包含了大多數(shù)開發(fā)人員已經(jīng)使用、正在使用或?qū)硪欢〞玫降母咝Чぞ?每個工具都給出了詳細(xì)的代碼示例,需要的朋友可以參考下
    2026-03-03
  • SpringBoot整合Spring Security構(gòu)建安全的Web應(yīng)用

    SpringBoot整合Spring Security構(gòu)建安全的Web應(yīng)用

    pring Security是一個強(qiáng)大的身份驗(yàn)證和訪問控制框架,本文主要介紹了SpringBoot整合Spring Security構(gòu)建安全的Web應(yīng)用,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01

最新評論

金华市| 涞源县| 景宁| 嘉荫县| 昂仁县| 通城县| 昌都县| 屏东县| 清苑县| 三门县| 灵宝市| 百色市| 资兴市| 来宾市| 阳西县| 安福县| 苍南县| 六安市| 大姚县| 隆子县| 富宁县| 三门峡市| 德州市| 施秉县| 海盐县| 南康市| 南华县| 弥勒县| 正安县| 四子王旗| 巴中市| 乌拉特中旗| 磴口县| 江都市| 鹰潭市| 浮山县| 湟源县| 合水县| 邯郸市| 庆安县| 阿荣旗|