若依框架中实现动态数据源切换

2025-06发布2次浏览

若依框架(RuoYi)是一款基于Spring Boot和Spring Cloud的开源快速开发平台,它提供了丰富的功能模块和灵活的扩展性。在实际项目中,动态数据源切换是一个常见的需求,例如多租户系统、分库分表场景等。本文将详细介绍如何在若依框架中实现动态数据源切换,包括技术原理、实现步骤以及代码示例。


一、动态数据源的基本原理

动态数据源的核心思想是根据业务逻辑或上下文环境,在运行时切换到不同的数据库连接。其实现主要依赖以下几个关键点:

  1. 抽象数据源管理:通过AbstractRoutingDataSource类(Spring提供的一个抽象类),定义数据源路由规则。
  2. 线程上下文绑定:使用ThreadLocal存储当前线程的数据源标识,确保每个线程能够独立切换数据源。
  3. 配置多个数据源:在Spring Boot中配置多个数据源,并通过动态路由加载指定的数据源。

二、实现步骤

1. 引入依赖

确保项目中已引入Spring Boot相关的依赖。如果需要操作数据库,还需引入对应的数据库驱动。以MySQL为例,pom.xml中的依赖如下:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

2. 配置多个数据源

application.yml中配置多个数据源信息。例如:

spring:
  datasource:
    master:
      url: jdbc:mysql://localhost:3306/master_db?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
    slave:
      url: jdbc:mysql://localhost:3306/slave_db?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver

3. 创建动态数据源类

继承AbstractRoutingDataSource并重写determineCurrentLookupKey方法,用于返回当前线程的数据源标识。

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }
}

4. 实现线程上下文工具类

使用ThreadLocal存储当前线程的数据源标识。

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();
    }
}

5. 配置数据源

编写配置类,将多个数据源注入到动态数据源中。

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
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;

@Configuration
public class DataSourceConfig {

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

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

    @Primary
    @Bean(name = "dynamicDataSource")
    public DynamicDataSource dynamicDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
                                             @Qualifier("slaveDataSource") DataSource slaveDataSource) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("master", masterDataSource);
        targetDataSources.put("slave", slaveDataSource);
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
        return dynamicDataSource;
    }
}

6. 切换数据源

在业务代码中调用DataSourceContextHolder切换数据源。例如:

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public void switchToMasterAndQuery() {
        DataSourceContextHolder.setDataSource("master");
        List<User> users = userRepository.findAll();
        DataSourceContextHolder.clearDataSource(); // 清除数据源上下文
    }

    public void switchToSlaveAndQuery() {
        DataSourceContextHolder.setDataSource("slave");
        List<User> users = userRepository.findAll();
        DataSourceContextHolder.clearDataSource();
    }
}

三、动态数据源切换流程图

以下是动态数据源切换的流程图,展示了从配置到切换的完整过程:

graph TD
    A[初始化多个数据源] --> B[注册到DynamicDataSource]
    B --> C[设置默认数据源]
    D[业务代码调用] --> E[切换数据源(使用ThreadLocal)]
    E --> F[执行数据库操作]
    F --> G[恢复默认数据源或清除上下文]

四、注意事项

  1. 线程安全问题ThreadLocal保证了线程级别的隔离,但在异步任务或线程池场景下需要注意清理上下文,避免数据源泄漏。
  2. 事务管理:动态数据源切换可能会影响事务的传播行为,需确保事务配置正确。
  3. 性能优化:对于频繁切换数据源的场景,可考虑缓存数据源连接池。