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

SpringBoot實(shí)現(xiàn)數(shù)據(jù)源動(dòng)態(tài)切換的最佳姿勢(shì)

 更新時(shí)間:2025年03月23日 08:44:32   作者:程序員蝸牛g  
這篇文章主要為大家詳細(xì)介紹一下SpringBoot實(shí)現(xiàn)數(shù)據(jù)源動(dòng)態(tài)切換的最佳姿勢(shì),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

在介紹動(dòng)態(tài)數(shù)據(jù)源之前,我們先一起來(lái)看看多數(shù)據(jù)源在 Spring Boot 中的實(shí)現(xiàn)方式。

1.1數(shù)據(jù)庫(kù)準(zhǔn)備

創(chuàng)建兩個(gè)庫(kù),分別是db_test_1db_test_2。db_test_1數(shù)據(jù)庫(kù)中創(chuàng)建一張用戶表,腳本如下:

db_test_2數(shù)據(jù)庫(kù)中創(chuàng)建另一張賬戶表,腳本如下:

1.2工程環(huán)境準(zhǔn)備

pom.xml中添加相關(guān)的依賴包,示例如下:

<!--spring boot核心-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<!--spring boot 測(cè)試-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<!--mysql 驅(qū)動(dòng)-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>
<!--mybatis-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.0.0</version>
</dependency>
<!--aspectj 注解代理-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency>

1.3編寫(xiě)多數(shù)據(jù)源

創(chuàng)建動(dòng)態(tài)數(shù)據(jù)源服務(wù)類

首先,創(chuàng)建一個(gè)DynamicDataSource類,并繼承AbstractRoutingDataSource抽象類,同時(shí)重寫(xiě)determineCurrentLookupKey()方法,代碼示例如下:

創(chuàng)建動(dòng)態(tài)數(shù)據(jù)源緩存類

創(chuàng)建一個(gè)DataSourceContextHolder類,用于緩存數(shù)據(jù)源,同時(shí)需要確保線程環(huán)境下安全

package com.example.dynamic.datasource.config;
 
publicclass DataSourceContextHolder {
 
    /**
     * 設(shè)置線程獨(dú)立變量,用于存儲(chǔ)數(shù)據(jù)源唯一標(biāo)記
     */
    privatestaticfinal ThreadLocal<String> DATASOURCE_HOLDER = new ThreadLocal<>();
 
    /**
     * 設(shè)置數(shù)據(jù)源
     * @param dataSourceName 數(shù)據(jù)源名稱
     */
    public static void set(String dataSourceName){
        DATASOURCE_HOLDER.set(dataSourceName);
    }
 
    /**
     * 獲取當(dāng)前線程的數(shù)據(jù)源
     * @return 數(shù)據(jù)源名稱
     */
    public static String get(){
        return DATASOURCE_HOLDER.get();
    }
 
    /**
     * 刪除當(dāng)前數(shù)據(jù)源
     */
    public static void remove(){
        DATASOURCE_HOLDER.remove();
    }
 
}

創(chuàng)建動(dòng)態(tài)數(shù)據(jù)源配置類

接著,創(chuàng)建一個(gè)DataSourceConfig配置類,設(shè)置動(dòng)態(tài)數(shù)據(jù)源相關(guān)的參數(shù),并注入到 Bean 工廠,代碼示例如下:

package com.example.dynamic.datasource.config;
 
@Configuration
publicclass DataSourceConfig {
 
    @Bean(name = "db1")
    @ConfigurationProperties(prefix = "spring.datasource.db1.druid")
    public DataSource db1(){
        return DruidDataSourceBuilder.create().build();
    }
 
    @Bean(name = "db2")
    @ConfigurationProperties(prefix = "spring.datasource.db2.druid")
    public DataSource db2(){
        return DruidDataSourceBuilder.create().build();
    }
 
    @Bean
    @Primary
    public DynamicDataSource createDynamicDataSource(){
        // 配置數(shù)據(jù)源集合,其中key代表數(shù)據(jù)源名稱,DataSourceContextHolder中緩存的就是這個(gè)key
        Map<Object,Object> dataSourceMap = new HashMap<>();
        dataSourceMap.put("db1",db1());
        dataSourceMap.put("db2",db2());
 
        // 注入動(dòng)態(tài)數(shù)據(jù)源到bean工廠
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 設(shè)置默認(rèn)數(shù)據(jù)源
        dynamicDataSource.setDefaultTargetDataSource(db1());
        // 設(shè)置動(dòng)態(tài)數(shù)據(jù)源集
        dynamicDataSource.setTargetDataSources(dataSourceMap);
        return dynamicDataSource;
    }
}

編寫(xiě)相關(guān)配置變量

根據(jù)上面的配置變量,我們還需要在application.properties文件中添加相關(guān)的數(shù)據(jù)源變量,內(nèi)容如下:

排除自動(dòng)裝配數(shù)據(jù)源

需要在注解@SpringBootApplication類上排除自動(dòng)裝配數(shù)據(jù)源配置,內(nèi)容如下:

1.4利用切面代理類設(shè)置數(shù)據(jù)源

在上文中,我們采用的是手動(dòng)方式來(lái)設(shè)置數(shù)據(jù)源,在實(shí)際的業(yè)務(wù)開(kāi)發(fā)中,我們通常會(huì)采用切面代理類來(lái)設(shè)置數(shù)據(jù)源,以便簡(jiǎn)化代碼復(fù)雜度。

創(chuàng)建數(shù)據(jù)源注解

首先,定義一個(gè)數(shù)據(jù)源注解來(lái)實(shí)現(xiàn)數(shù)據(jù)源的切換,同時(shí)配置一個(gè)默認(rèn)的數(shù)據(jù)源名稱,代碼示例如下:

package com.example.dynamic.datasource.config.aop;
 
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DbSource {
 
    /**
     * 數(shù)據(jù)源key值
     * @return
     */
    String value() default "db1";
}

編寫(xiě)數(shù)據(jù)源代理類

接著,基于@DbSource注解,創(chuàng)建一個(gè) AOP 代理類,所有配置該注解的方法都會(huì)被前后攔截,代碼示例如下:

package com.example.dynamic.datasource.config.aop;
 
@Order(1)
@Aspect
@Component
publicclass DbSourceAspect {
 
    privatestaticfinal Logger LOGGER = LoggerFactory.getLogger(DbSourceAspect.class);
 
 
    @Pointcut("@annotation(com.example.dynamic.datasource.config.aop.DbSource)")
    public void dynamicDataSource(){}
 
    @Around("dynamicDataSource()")
    public Object datasourceAround(ProceedingJoinPoint point) throws Throwable {
        // 獲取要切換的數(shù)據(jù)源名稱
        MethodSignature methodSignature = (MethodSignature)point.getSignature();
        Method method = methodSignature.getMethod();
        DbSource dbSource = method.getAnnotation(DbSource.class);
        LOGGER.info("select dataSource:" + dbSource.value());
        DataSourceContextHolder.set(dbSource.value());
        try {
            return point.proceed();
        } finally {
            DataSourceContextHolder.remove();
        }
    }
}

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

最后,在需要的方法上配置相關(guān)的數(shù)據(jù)源注解即可。

@Service
public class UserInfoService {
 
    @Autowired
    private UserInfoMapper userInfoMapper;
 
    @Transactional
    @DbSource(value = "db1")
    public void add(UserInfo entity){
        userInfoMapper.insert(entity);
    }
}

賬戶服務(wù)類,代碼示例如下:

@Service
public class AccountInfoService {
 
    @Autowired
    private AccountInfoMapper accountInfoMapper;
 
    @Transactional
    @DbSource(value = "db2")
    public void add(AccountInfo entity){
        accountInfoMapper.insert(entity);
    }
}

采用 aop 代理的方式來(lái)切換數(shù)據(jù)源,業(yè)務(wù)實(shí)現(xiàn)上會(huì)更加的靈活。

在上文中,我們介紹了多數(shù)據(jù)源的配置實(shí)現(xiàn)方式,這種配置方式有一個(gè)不好的地方在于:配置文件都是寫(xiě)死的。

能不能改成動(dòng)態(tài)的加載數(shù)據(jù)源呢,下面我們一起來(lái)看看相關(guān)的具體實(shí)現(xiàn)方式

2.1數(shù)據(jù)庫(kù)準(zhǔn)備

首先,我們需要準(zhǔn)備一張數(shù)據(jù)源配置表。新建一個(gè)test_db數(shù)據(jù)庫(kù),然后在數(shù)據(jù)庫(kù)中創(chuàng)建一張數(shù)據(jù)源配置表,腳本如下:

CREATE TABLE`tb_db_info` (
`id`int(11) unsignedNOTNULL AUTO_INCREMENT,
`db_name`varchar(50) DEFAULTNULL,
`db_url`varchar(200) DEFAULTNULL,
`driver_class_name`varchar(100) DEFAULTNULL,
`username`varchar(80) DEFAULTNULL,
`password`varchar(80) DEFAULTNULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3DEFAULTCHARSET=utf8mb4;

最后,初始化兩條數(shù)據(jù),方便后續(xù)數(shù)據(jù)源的查詢。

2.2修改全局配置文件

我們還是以上文介紹的工程為例,把之前自定義的配置參數(shù)刪除掉,重新基于 Spring Boot 約定的配置方式,添加相關(guān)的數(shù)據(jù)源參數(shù),內(nèi)容如下:

# 配置默認(rèn)數(shù)據(jù)源
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

2.3編寫(xiě)相關(guān)的服務(wù)類

基于數(shù)據(jù)庫(kù)中tb_db_info表,編寫(xiě)相關(guān)的查詢邏輯,代碼示例如下:

package com.example.dynamic.datasource.entity;
 
publicclass DbInfo {
 
    /**
     * 主鍵ID
     */
    private Integer id;
 
    /**
     * 數(shù)據(jù)庫(kù)key,即保存Map中的key
     */
    private String dbName;
 
    /**
     * 數(shù)據(jù)庫(kù)地址
     */
    private String dbUrl;
 
    /**
     * 數(shù)據(jù)庫(kù)驅(qū)動(dòng)
     */
    private String driverClassName;
 
    /**
     * 數(shù)據(jù)庫(kù)用戶名
     */
    private String username;
 
    /**
     * 數(shù)據(jù)庫(kù)密碼
     */
    private String password;
 
    // set、get方法等...
}
public interface DbInfoMapper {
 
    List<DbInfo> findAll();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.dynamic.datasource.mapper.DbInfoMapper">
 
    <select id="findAll" resultType="com.example.dynamic.datasource.entity.DbInfo">
        select
        id
        ,db_name as dbName
        ,db_url as dbUrl
        ,driver_class_name as driverClassName
        ,username
        ,password
        from tb_db_info
        order by id
    </select>
</mapper>

2.4修改動(dòng)態(tài)數(shù)據(jù)源服務(wù)類

對(duì)DynamicDataSource類進(jìn)行一些調(diào)整,代碼如下:

public class DynamicDataSource extends AbstractRoutingDataSource {
 
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.get();
    }
 
    /**
     * 重新加載數(shù)據(jù)源集合
     * @param dbList
     */
    public void loadDataSources(List<DbInfo> dbList){
        try {
            Map<Object, Object> targetDataSourceMap = new HashMap<>();
            for (DbInfo source : dbList) {
                // 初始化數(shù)據(jù)源
                DruidDataSource dataSource = new DruidDataSource();
                dataSource.setDriverClassName(source.getDriverClassName());
                dataSource.setUrl(source.getDbUrl());
                dataSource.setUsername(source.getUsername());
                dataSource.setPassword(source.getPassword());
                dataSource.setInitialSize(1);
                dataSource.setMinIdle(1);
                dataSource.setMaxActive(5);
                dataSource.setTestWhileIdle(true);
                dataSource.setTestOnBorrow(true);
                dataSource.setValidationQuery("select 1 ");
                dataSource.init();
                targetDataSourceMap.put(source.getDbName(), dataSource);
            }
            super.setTargetDataSources(targetDataSourceMap);
            // 重新初始化resolvedDataSources對(duì)象
            super.afterPropertiesSet();
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

2.5修改動(dòng)態(tài)數(shù)據(jù)源配置類

對(duì)DataSourceConfig類也需要進(jìn)行一些調(diào)整,通過(guò) Spring Boot 默認(rèn)的數(shù)據(jù)源配置類初始化一個(gè)數(shù)據(jù)源實(shí)例對(duì)象,代碼如下:

@Configuration
publicclass DataSourceConfig {
 
    @Autowired
    private DataSourceProperties basicProperties;
 
    /**
     * 注入動(dòng)態(tài)數(shù)據(jù)源
     * @param dataSource
     * @return
     */
    @Bean
    @Primary
    public DynamicDataSource dynamicDataSource(){
        // 獲取初始數(shù)據(jù)源
        DataSource defaultDataSource = basicProperties.initializeDataSourceBuilder().build();
 
        Map<Object,Object> targetDataSources = new HashMap<>();
        targetDataSources.put("defaultDataSource", defaultDataSource);
        // 注入動(dòng)態(tài)數(shù)據(jù)源
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 設(shè)置默認(rèn)數(shù)據(jù)源
        dynamicDataSource.setDefaultTargetDataSource(defaultDataSource);
        // 設(shè)置動(dòng)態(tài)數(shù)據(jù)源集
        dynamicDataSource.setTargetDataSources(targetDataSources);
        return dynamicDataSource;
    }
}

2.6配置啟動(dòng)時(shí)加載數(shù)據(jù)源服務(wù)類

以上的配置調(diào)整完成之后,我們還需要配置一個(gè)服務(wù)啟動(dòng)監(jiān)聽(tīng)類,將從數(shù)據(jù)庫(kù)中查詢到的數(shù)據(jù)配置信息加載到DynamicDataSource對(duì)象中,代碼示例如下:

2.7調(diào)整 SpringBootApplication 注解配置

以上的實(shí)現(xiàn)方式,因?yàn)閱?dòng)的時(shí)候,采用的是 Spring Boot 默認(rèn)的數(shù)據(jù)源配置實(shí)現(xiàn),因此無(wú)需排除DataSourceAutoConfiguration類,可以將相關(guān)參數(shù)移除掉。

到此這篇關(guān)于SpringBoot實(shí)現(xiàn)數(shù)據(jù)源動(dòng)態(tài)切換的最佳姿勢(shì)的文章就介紹到這了,更多相關(guān)SpringBoot數(shù)據(jù)源動(dòng)態(tài)切換內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Kafka中使用Avro序列化和反序列化詳解

    Kafka中使用Avro序列化和反序列化詳解

    這篇文章主要介紹了Kafka中使用Avro序列化和反序列化詳解,由于Kafka中的數(shù)據(jù)都是字節(jié)數(shù)組,在將消息發(fā)送到Kafka之前需要先將數(shù)據(jù)序列化為字節(jié)數(shù)組, 序列化器的作用就是用于序列化要發(fā)送的消息的,需要的朋友可以參考下
    2023-12-12
  • Spring整合redis的操作代碼

    Spring整合redis的操作代碼

    這篇文章主要介紹了Spring整合redis的操作代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2022-02-02
  • java獲取兩個(gè)數(shù)組中不同數(shù)據(jù)的方法

    java獲取兩個(gè)數(shù)組中不同數(shù)據(jù)的方法

    這篇文章主要介紹了java獲取兩個(gè)數(shù)組中不同數(shù)據(jù)的方法,實(shí)例分析了java操作數(shù)組的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-03-03
  • Java實(shí)現(xiàn)短信驗(yàn)證碼服務(wù)的完整代碼示例

    Java實(shí)現(xiàn)短信驗(yàn)證碼服務(wù)的完整代碼示例

    這篇文章主要介紹了Java實(shí)現(xiàn)短信驗(yàn)證碼服務(wù)的完整代碼示例,文中使用阿里云的短信服務(wù)進(jìn)行應(yīng)用開(kāi)發(fā)的流程,包括將屬性寫(xiě)入application.yml配置文件,定義類并指定配置文件,注入實(shí)體類對(duì)象等等,需要的朋友可以參考下
    2024-09-09
  • java枚舉enum,根據(jù)value值獲取key鍵的操作

    java枚舉enum,根據(jù)value值獲取key鍵的操作

    這篇文章主要介紹了java枚舉enum,根據(jù)value值獲取key鍵的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-02-02
  • 在Java中按值調(diào)用和按引用調(diào)用

    在Java中按值調(diào)用和按引用調(diào)用

    這篇文章主要介紹了Java中的按值調(diào)用和按引用調(diào)用,一種是按值調(diào)用,另一種是按引用調(diào)用,這兩種方式通常根據(jù)作為輸入或參數(shù)傳遞給它們的值的類型來(lái)區(qū)分,下文相關(guān)的更多詳細(xì)資料感興趣的小伙伴可以參考一下
    2022-04-04
  • Spring中容器創(chuàng)建的四種方式示例

    Spring中容器創(chuàng)建的四種方式示例

    這篇文章主要介紹了Spring中容器創(chuàng)建的四種方式示例,Spring容器是Spring框架的核心部分,它負(fù)責(zé)管理和組織應(yīng)用程序中的對(duì)象,它提供了一種輕量級(jí)的、非侵入式的方式來(lái)實(shí)現(xiàn)對(duì)象的創(chuàng)建、依賴注入和生命周期管理,需要的朋友可以參考下
    2023-10-10
  • Java數(shù)據(jù)結(jié)構(gòu)之雙向鏈表的實(shí)現(xiàn)

    Java數(shù)據(jù)結(jié)構(gòu)之雙向鏈表的實(shí)現(xiàn)

    相較單鏈表,雙向鏈表除了data與next域,還多了一個(gè)pre域用于表示每個(gè)節(jié)點(diǎn)的前一個(gè)元素。這樣做給雙向鏈表帶來(lái)了很多優(yōu)勢(shì)。本文主要介紹了雙向鏈表的實(shí)現(xiàn),需要的可以參考一下
    2022-10-10
  • MyBatis游標(biāo)Cursor在Oracle數(shù)據(jù)庫(kù)上的測(cè)試方式

    MyBatis游標(biāo)Cursor在Oracle數(shù)據(jù)庫(kù)上的測(cè)試方式

    這篇文章主要介紹了MyBatis游標(biāo)Cursor在Oracle數(shù)據(jù)庫(kù)上的測(cè)試方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • MySQL中關(guān)鍵字UNION和UNION ALL的區(qū)別

    MySQL中關(guān)鍵字UNION和UNION ALL的區(qū)別

    本文主要介紹了MySQL中關(guān)鍵字UNION和UNION ALL的區(qū)別,深入探討UNION和UNION ALL的定義、用法、主要區(qū)別,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-06-06

最新評(píng)論