Spring Boot实现多数据源连接和切换

文章目录
  • 前言
  • 一、多数据源配置与切换方案
  • 二、实现步骤
    • [1. 创建多个 `DataSource` 配置类](#1. 创建多个 DataSource 配置类)
    • [2. 创建 `DataSource` 配置类](#2. 创建 DataSource 配置类)
    • [3. 创建动态数据源路由类](#3. 创建动态数据源路由类)
    • [4. 实现 `DynamicDataSource` 类](#4. 实现 DynamicDataSource 类)
    • [5. 创建 `DataSourceContextHolder` 来存储当前的数据源标识](#5. 创建 DataSourceContextHolder 来存储当前的数据源标识)
    • [6. AOP 方式切换数据源](#6. AOP 方式切换数据源)
    • [7. 自定义注解来指定数据源](#7. 自定义注解来指定数据源)
    • [8. 在 Service 层使用注解指定数据源](#8. 在 Service 层使用注解指定数据源)
  • 总结

前言

在 Spring Boot 中实现多数据源连接和切换,可以通过以下几种方案来实现,具体取决于项目的需求、数据库的使用模式和管理的复杂性。以下是一个常见的多数据源切换的实现方案,使用 AbstractRoutingDataSource 来动态选择数据源。


一、多数据源配置与切换方案

在多数据源场景中,通常有如下步骤:

  • 配置多个数据源的 DataSource bean。
  • 使用 AbstractRoutingDataSource 来动态切换数据源。
  • 使用 ThreadLocal 存储当前的数据库类型或数据源标识符。
  • 配置数据源切换的逻辑,例如基于当前的用户、请求路径、服务标识等来选择不同的数据源。

二、实现步骤

1. 创建多个 DataSource 配置类

首先,为每个数据源创建单独的配置类,通常你会在 application.ymlapplication.properties 中配置每个数据源的连接信息。

复制代码
spring:
  datasource:
    # 默认数据源配置
    primary:
      url: jdbc:mysql://localhost:3306/primary_db
      username: root
      password: password
      driver-class-name: com.mysql.cj.jdbc.Driver
      hikari:
        maximum-pool-size: 10
    
    # 第二数据源配置
    secondary:
      url: jdbc:mysql://localhost:3306/secondary_db
      username: root
      password: password
      driver-class-name: com.mysql.cj.jdbc.Driver
      hikari:
        maximum-pool-size: 10

2. 创建 DataSource 配置类

复制代码
@Configuration
@EnableTransactionManagement
public class DataSourceConfig {

    @Bean(name = "primaryDataSource")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }
}

3. 创建动态数据源路由类

AbstractRoutingDataSource 允许我们在运行时根据某些条件动态选择数据源。

复制代码
@Configuration
public class DynamicDataSourceConfig {

    @Autowired
    @Qualifier("primaryDataSource")
    private DataSource primaryDataSource;

    @Autowired
    @Qualifier("secondaryDataSource")
    private DataSource secondaryDataSource;

    @Bean
    public DataSource dataSource() {
        // 创建一个路由数据源
        DynamicDataSource dataSource = new DynamicDataSource();
        dataSource.setDefaultTargetDataSource(primaryDataSource); // 默认数据源
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("primary", primaryDataSource);
        targetDataSources.put("secondary", secondaryDataSource);
        dataSource.setTargetDataSources(targetDataSources);
        return dataSource;
    }
}

4. 实现 DynamicDataSource

DynamicDataSource 是继承自 AbstractRoutingDataSource,它通过 determineCurrentLookupKey() 方法来动态确定当前的数据源。

复制代码
public class DynamicDataSource extends AbstractRoutingDataSource {

    // 从 ThreadLocal 获取当前的数据库标识
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceType();
    }
}

5. 创建 DataSourceContextHolder 来存储当前的数据源标识

使用 ThreadLocal 来保持当前线程的数据库标识,以便在不同的数据源之间切换。

复制代码
public class DataSourceContextHolder {

    // 使用 ThreadLocal 存储当前线程的数据源标识
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

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

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

6. AOP 方式切换数据源

为了在运行时动态切换数据源,通常会使用 AOP 切面来拦截方法执行并指定数据源。

复制代码
@Aspect
@Component
public class DataSourceAspect {

    // 通过注解来指定使用哪个数据源
    @Before("@annotation(dataSource)")
    public void switchDataSource(DataSourceType dataSource) {
        // 切换数据源
        DataSourceContextHolder.setDataSourceType(dataSource.value());
    }

    @After("@annotation(dataSource)")
    public void clearDataSource(DataSourceType dataSource) {
        // 清理数据源标识,避免影响其他线程
        DataSourceContextHolder.clearDataSourceType();
    }
}

7. 自定义注解来指定数据源

创建一个自定义注解 DataSourceType,用于指定当前方法执行时需要使用的数据源。

复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSourceType {

    String value() default "primary";  // 数据源标识,默认使用primary数据源
}

8. 在 Service 层使用注解指定数据源

在 Service 层,可以使用刚才创建的 @DataSourceType 注解来指定不同的方法使用不同的数据源。

复制代码
@Service
public class UserService {

    @DataSourceType("primary")
    public List<User> getPrimaryUsers() {
        // 查询主数据库
        return userRepository.findAll();
    }

    @DataSourceType("secondary")
    public List<User> getSecondaryUsers() {
        // 查询次数据库
        return secondaryUserRepository.findAll();
    }
}

总结

  1. 数据源配置 :为每个数据源配置 DataSource Bean。
  2. 动态数据源路由 :使用 AbstractRoutingDataSource 来实现动态切换数据源。
  3. ThreadLocal存储 :使用 ThreadLocal 存储和获取当前线程的数据源标识。
  4. AOP切换数据源:使用 AOP 来拦截方法并切换数据源。
  5. 注解方式指定数据源:通过自定义注解来指定方法使用的具体数据源。

这种方式比较灵活,能够在运行时根据业务需求选择不同的数据源,适用于多数据源的场景,尤其是分库分表、读写分离等复杂应用场景。

相关推荐
李广坤1 分钟前
JAVA线程池详解
后端
调试人生的显微镜7 分钟前
深入剖析 iOS 26 系统流畅度,多工具协同监控与性能优化实践
后端
蹦跑的蜗牛8 分钟前
Spring Boot使用Redis实现消息队列
spring boot·redis·后端
非凡ghost17 分钟前
HWiNFO(专业系统信息检测工具)
前端·javascript·后端
非凡ghost19 分钟前
FireAlpaca(免费数字绘图软件)
前端·javascript·后端
非凡ghost26 分钟前
Sucrose Wallpaper Engine(动态壁纸管理工具)
前端·javascript·后端
间彧29 分钟前
从零到一搭建Spring Cloud Alibbaba项目
后端
楼田莉子31 分钟前
C++学习:C++11关于类型的处理
开发语言·c++·后端·学习
LSTM9734 分钟前
使用 Java 对 PDF 添加水印:提升文档安全与版权保护
后端
该用户已不存在34 分钟前
Gemini CLI 扩展,把Nano Banana 搬到终端
前端·后端·ai编程