0
点赞
收藏
分享

微信扫一扫

在 SpringBoot 项目中如何动态切换数据源、数据库?

Spring Boot 项目中,动态切换数据源(数据库)通常需要使用 AbstractRoutingDataSource,结合 Spring AOPThreadLocal 机制来动态选择不同的数据源。以下是实现步骤:

1. 引入必要的依赖

如果使用 Spring Boot + MyBatis + 数据库,通常需要添加如下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.2</version>
</dependency>

2. 定义动态数据源上下文

创建 DynamicDataSourceContextHolder,使用 ThreadLocal 记录当前线程的数据源。

public class DynamicDataSourceContextHolder {
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    // 设置数据源
    public static void setDataSourceKey(String key) {
        CONTEXT_HOLDER.set(key);
    }

    // 获取当前数据源
    public static String getDataSourceKey() {
        return CONTEXT_HOLDER.get();
    }

    // 清除数据源
    public static void clearDataSourceKey() {
        CONTEXT_HOLDER.remove();
    }
}

3. 创建动态数据源

继承 AbstractRoutingDataSource,实现 determineCurrentLookupKey() 方法。

@Component
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceKey();
    }
}

4. 配置多个数据源

DataSourceConfig 配置类中,初始化多个数据源,并将 DynamicDataSource 设置为主数据源。

@Configuration
public class DataSourceConfig {

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

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

    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource(@Qualifier("dataSource1") DataSource dataSource1,
                                        @Qualifier("dataSource2") DataSource dataSource2) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("db1", dataSource1);
        targetDataSources.put("db2", dataSource2);

        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(dataSource1); // 默认数据源
        return dynamicDataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) throws Exception {
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dynamicDataSource);
        return sessionFactoryBean.getObject();
    }
}

配置 application.yml

spring:
  datasource:
    db1:
      url: jdbc:mysql://localhost:3306/db1
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
    db2:
      url: jdbc:mysql://localhost:3306/db2
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver

5. 通过 AOP 动态切换数据源

使用自定义注解 + AOP 来实现方法级别的数据源切换。

5.1 创建自定义注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    String value() default "db1"; // 默认数据源
}

5.2 创建 AOP 切面

@Aspect
@Component
public class DynamicDataSourceAspect {

    @Pointcut("@annotation(com.example.datasource.DataSource)")
    public void dataSourcePointCut() {}

    @Around("dataSourcePointCut() && @annotation(dataSource)")
    public Object around(ProceedingJoinPoint point, DataSource dataSource) throws Throwable {
        // 切换数据源
        DynamicDataSourceContextHolder.setDataSourceKey(dataSource.value());
        try {
            return point.proceed();
        } finally {
            DynamicDataSourceContextHolder.clearDataSourceKey();
        }
    }
}

6. 在业务层使用动态数据源

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @DataSource("db1")  // 使用 db1 数据库
    public List<User> getUsersFromDB1() {
        return userMapper.findAll();
    }

    @DataSource("db2")  // 使用 db2 数据库
    public List<User> getUsersFromDB2() {
        return userMapper.findAll();
    }
}

7. 编写 MyBatis Mapper

@Mapper
public interface UserMapper {
    @Select("SELECT * FROM user")
    List<User> findAll();
}

8. 启动项目并测试

启动 Spring Boot 应用后,可以调用:

  • getUsersFromDB1() 方法获取 db1 数据库的数据
  • getUsersFromDB2() 方法获取 db2 数据库的数据

这样就实现了 Spring Boot动态数据源切换

总结

  1. 使用 ThreadLocal 记录当前数据源,避免多线程问题。
  2. 继承 AbstractRoutingDataSource 实现动态数据源
  3. DataSourceConfig 中配置多个数据源,并使用 DynamicDataSource 进行管理。
  4. 通过 AOP 和自定义注解动态切换数据源,使得方法级别的数据源切换更方便。
  5. 在业务层使用注解 @DataSource("dbX") 进行数据源切换,实现灵活操作。
举报

相关推荐

0 条评论