使用Spring Boot实现动态数据源切换

科技   2024-11-08 08:00   河北  


前言

在复杂的业务场景中,一个应用可能需要连接多个数据库,并根据不同的业务需求动态地切换数据源。Spring Boot提供了强大的数据访问和配置管理能力,使得实现动态数据源切换变得相对简单。本文将介绍如何在Spring Boot项目中实现动态数据源切换,并提供示例代码。

1. 引入必要的依赖

首先,在你的Spring Boot项目的pom.xml文件中引入必要的依赖。这些依赖包括Spring Boot Starter JDBC、数据库驱动、以及一个动态数据源解决方案(如dynamic-datasource-spring-boot-starter,或者你可以选择自定义实现)。

<dependencies>
    <!-- Spring Boot Starter JDBC -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

    <!-- MySQL 数据库驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!-- 动态数据源解决方案 -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
        <version>最新版本</version>
    </dependency>
</dependencies>

2. 配置数据源

application.ymlapplication.properties文件中配置多个数据源。以下是一个application.yml的配置示例:

2. 配置数据源
application.ymlapplication.properties文件中配置多个数据源。以下是一个application.yml的配置示例:

3. 使用动态数据源

如果你使用dynamic-datasource-spring-boot-starter,你不需要编写太多的切换逻辑,因为它已经提供了这些功能。你只需要在你的Service层或者需要切换数据源的任何地方,使用@DS注解来指定数据源。

import org.springframework.stereotype.Service;
import com.baomidou.dynamic.datasource.annotation.DS;

@Service
public class SomeService {

    @DS("slave")
    public List<SomeData> findDataFromSlave() {
        // 这里会切换到slave数据源
        return someRepository.findAll();
    }

    @DS("master")
    public void updateDataInMaster(SomeData data) {
        // 这里会切换到master数据源
        someRepository.save(data);
    }
}

4. 自定义实现动态数据源切换(可选)

如果你不使用dynamic-datasource-spring-boot-starter,你可以自定义实现动态数据源切换。这通常涉及到以下几个步骤:

  • 创建一个继承自AbstractRoutingDataSource的类,并在其中实现determineCurrentLookupKey()方法,该方法用于决定使用哪个数据源。

  • 创建一个数据源上下文类,用于存储当前线程使用的数据源。

  • 在配置类中配置多个数据源,并将它们注册到动态数据源中。

以下是一个自定义实现的示例代码:

// 数据源上下文类
public class DataSourceContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDataSource(String dataSource) {
        contextHolder.set(dataSource);
    }

    public static String getDataSource() {
        return contextHolder.get();
    }

    public static void clearDataSource() {
        contextHolder.remove();
    }
}

// 动态数据源类
public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }
}

// 配置类
@Configuration
public class DataSourceConfig {

    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public DataSource dataSource() {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("master", masterDataSource());
        targetDataSources.put("slave", slaveDataSource());

        DynamicDataSource dataSource = new DynamicDataSource();
        dataSource.setDefaultTargetDataSource(masterDataSource());
        dataSource.setTargetDataSources(targetDataSources);
        return dataSource;
    }
}

// 使用AOP切换数据源(示例)
@Aspect
@Component
public class DataSourceAspect {

    @Before("@annotation(dataSource)")
    public void changeDataSource(JoinPoint point, DataSource dataSource) throws Throwable {
        DataSourceContextHolder.setDataSource(dataSource.value());
    }

    @After("@annotation(dataSource)")
    public void clearDataSource(JoinPoint point, DataSource dataSource) throws Throwable {
        DataSourceContextHolder.clearDataSource();
    }
}

// 自定义注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
    String value() default "master";
}

在Service层使用自定义注解来指定数据源:

@Service
public class SomeService {

    @DataSource("slave")
    public List<SomeData> findDataFromSlave() {
        // 这里会切换到slave数据源
        return someRepository.findAll();
    }

    @DataSource("master")
    public void updateDataInMaster(SomeData data) {
        // 这里会切换到master数据源
        someRepository.save(data);
    }
}





Java技术前沿
专注分享Java技术,包括但不限于 SpringBoot,SpringCloud,Docker,消息中间件等。
 最新文章