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
相关产品推荐
相关产品推荐

