You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot如何通过编程方式切换数据库?从db1切换至同表结构的db2

要实现编程式切换到db2,最常用的方案是基于Spring的AbstractRoutingDataSource实现动态数据源路由,下面一步步给你讲具体实现:

实现步骤

1. 配置多数据源信息

先修改你的application.properties,保留原db1配置的同时,添加db2的数据源配置:

# 主数据源(默认使用db1)
spring.datasource.db1.url=jdbc:postgresql://172.50.25.20:5432/db1
spring.datasource.db1.username=your_username
spring.datasource.db1.password=your_password
spring.datasource.db1.driver-class-name=org.postgresql.Driver

# 切换目标数据源(db2)
spring.datasource.db2.url=jdbc:postgresql://172.50.25.20:5432/db2
spring.datasource.db2.username=your_username
spring.datasource.db2.password=your_password
spring.datasource.db2.driver-class-name=org.postgresql.Driver

2. 创建数据源上下文持有者

用ThreadLocal存储当前线程绑定的数据源标识,保证线程安全:

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

    // 设置当前要使用的数据源key
    public static void setDataSourceKey(String key) {
        CONTEXT_HOLDER.set(key);
    }

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

    // 清除数据源key,避免内存泄漏
    public static void clearDataSourceKey() {
        CONTEXT_HOLDER.remove();
    }
}

3. 实现动态数据源路由类

继承Spring提供的AbstractRoutingDataSource,它会根据我们设置的key自动切换数据源:

@Component
public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        // 返回当前线程绑定的数据源标识
        return DataSourceContextHolder.getDataSourceKey();
    }
}

4. 配置数据源Bean

把两个数据源和动态数据源注册到Spring容器中:

@Configuration
public class DataSourceConfig {

    // 配置db1数据源
    @Bean(name = "db1DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.db1")
    public DataSource db1DataSource() {
        return DataSourceBuilder.create().build();
    }

    // 配置db2数据源
    @Bean(name = "db2DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.db2")
    public DataSource db2DataSource() {
        return DataSourceBuilder.create().build();
    }

    // 配置动态数据源,默认使用db1
    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource(@Qualifier("db1DataSource") DataSource db1DataSource,
                                        @Qualifier("db2DataSource") DataSource db2DataSource) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 映射数据源key与对应的数据源实例
        Map<Object, Object> dataSourceMap = new HashMap<>();
        dataSourceMap.put("db1", db1DataSource);
        dataSourceMap.put("db2", db2DataSource);
        dynamicDataSource.setTargetDataSources(dataSourceMap);
        // 设置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(db1DataSource);
        return dynamicDataSource;
    }

    // 配置SqlSessionFactory(如果使用MyBatis)
    @Bean
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) throws Exception {
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dynamicDataSource);
        // 可添加你的mapper文件路径,比如:classpath:mapper/*.xml
        // sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        return sessionFactoryBean.getObject();
    }
}

5. 编程式切换数据源

在需要操作db2的代码块中,手动设置数据源key,执行完后务必清除:

// 切换到db2数据源
DataSourceContextHolder.setDataSourceKey("db2");
try {
    // 执行针对db2的数据库操作,比如查询、更新
    List<User> db2Users = userMapper.selectAll();
    // ...
} finally {
    // 必须清除,避免影响后续线程的数据源使用
    DataSourceContextHolder.clearDataSourceKey();
}

进阶:用注解+AOP简化切换

如果不想每次都写try-finally,可以用自定义注解配合AOP自动完成切换:

5.1 自定义数据源注解

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

5.2 编写AOP切面

@Aspect
@Component
public class DataSourceAspect {

    // 拦截带有@DataSource注解的方法,切换数据源
    @Before("@annotation(dataSource)")
    public void beforeSwitchDataSource(JoinPoint point, DataSource dataSource) {
        String dataSourceKey = dataSource.value();
        DataSourceContextHolder.setDataSourceKey(dataSourceKey);
    }

    // 方法执行完成后清除数据源key
    @After("@annotation(dataSource)")
    public void afterSwitchDataSource(JoinPoint point, DataSource dataSource) {
        DataSourceContextHolder.clearDataSourceKey();
    }
}

5.3 使用注解切换

现在只需在方法上添加注解,就会自动切换到对应数据源:

@DataSource("db2")
public List<User> getDb2Users() {
    return userMapper.selectAll();
}

注意事项

  • 事务限制:如果在一个事务方法内切换数据源,切换不会生效——因为事务开启后数据源就被固定了。跨数据源操作要避免在同一事务中执行。
  • ThreadLocal清理:必须确保操作完成后清除ThreadLocal中的数据源key,否则可能导致内存泄漏或后续线程复用错误数据源。
  • JPA适配:如果使用Spring Data JPA,需要调整EntityManagerFactory的配置,将数据源替换为动态数据源,原理和MyBatis一致。

内容的提问来源于stack exchange,提问作者nix86

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:55:23