SpringBoot 多数据源配置

一. 创建动态数据源和数据源切换器

  1. 创建一个继承自AbstractRoutingDataSource的动态数据源类,用于根据当前线程的上下文选择数据源:

    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    public class DynamicDataSource extends AbstractRoutingDataSource {
    
     @Override
     protected Object determineCurrentLookupKey() {
         return DataSourceContextHolder.getDataSourceKey();
     }
    }
  2. 创建一个DataSourceContextHolder类,用于保存当前线程使用的数据源标识:

    public class DataSourceContextHolder {
    
     private static final ThreadLocal<String> dataSourceKey = new ThreadLocal<>();
    
     public static void setDataSourceKey(String key) {
         dataSourceKey.set(key);
     }
    
     public static String getDataSourceKey() {
         return dataSourceKey.get();
     }
    
     public static void clearDataSourceKey() {
         dataSourceKey.remove();
     }
    }

二. 数据源注入

  1. 在Spring Boot的配置类中配置多个数据源,并将它们添加到DynamicDataSource中:

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    @Configuration
    public class DataSourceConfig {
    
     @Bean(name = "dataSource1")
     @ConfigurationProperties("spring.datasource.dataSource1")
     public DataSource dataSource1() {
         // 配置第一个数据源
         // ...
     }
    
     @Bean(name = "dataSource2")
     @ConfigurationProperties("spring.datasource.dataSource2")
     public DataSource dataSource2() {
         // 配置第二个数据源
         // ...
     }
    
     @Primary
     @Bean(name = "datasource")
     public AbstractRoutingDataSource routingDataSource() {
         DynamicDataSource dynamicDataSource = new DynamicDataSource();
         Map<Object, Object> dataSourceMap = new HashMap<>();
         dataSourceMap.put("dataSource1", dataSource1());
         dataSourceMap.put("dataSource2", dataSource2());
         dynamicDataSource.setTargetDataSources(dataSourceMap);
         dynamicDataSource.setDefaultTargetDataSource(dataSource1());
         return dynamicDataSource;
     }
    }
  2. 移除SpringBoot中关于DataSource的自动配置, 解决循环依赖问题

    @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
    public class Application {
    
     public static void main(String[] args) {
         SpringApplication.run(Application.class, args);
     }
    
    }

三. 在业务中切换数据源

  1. 在需要切换数据源的地方,使用DataSourceContextHolder.setDataSourceKey("dataSource1")来设置当前线程使用的数据源标识:

    DataSourceContextHolder.setDataSourceKey("dataSource1");
    ds1Service.findAll();
    DataSourceContextHolder.setDataSourceKey("dataSource2");
    ds2Service.findAll();
    DataSourceContextHolder.clearDataSourceKey();
    确保在使用完数据源后,及时调用DataSourceContextHolder.clearDataSourceKey()来清除当前线程的数据源标识,以避免影响其他线程。

四. 拓展: 使用Interceptor为请求动态注入数据源

  1. 定义DataSourceInterceptor拦截器, 用来设置当前线程的数据源.

    public class DataSourceInterceptor extends HandlerInterceptorAdapter {
    
     private DynamicDataSource dynamicDataSource;
    
     public DataSourceInterceptor(DynamicDataSource dynamicDataSource) {
         this.dynamicDataSource = dynamicDataSource;
     }
    
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
         String dataSourceKey = determineDataSourceKey(request);
         DataSourceContextHolder.setDataSourceKey(dataSourceKey);
         return true;
     }
    
     private String determineDataSourceKey(HttpServletRequest request) {
         // 根据请求信息来确定数据源的标识
         // 这里可以根据需要自行实现逻辑,例如根据请求参数、请求头等来决定使用哪个数据源
         // 返回的数据源标识需要与之前配置的数据源名称对应
         return "dataSource1";
     }
    
     @Override
     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
         DataSourceContextHolder.clearDataSourceKey();
     }
    }
    preHandle()方法中,可以根据请求信息来确定当前请求应该使用的数据源,然后通过DataSourceContextHolder.setDataSourceKey(dataSourceKey)方法将数据源标识设置到DataSourceContextHolder中。在afterCompletion()方法中,清除了DataSourceContextHolder中的数据源标识,以确保每个请求结束后都能正确地清除数据源设置。
  2. 注册数据源切换拦截器

    该拦截器用于在每个请求前设置当前线程的数据源。
    @Configuration
    public class AppConfig implements WebMvcConfigurer {
    
        @Autowired
        private DynamicDataSource dynamicDataSource;
    
        @Bean
        public DataSourceTransactionManager transactionManager() {
            return new DataSourceTransactionManager(dynamicDataSource);
        }
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new DataSourceInterceptor(dynamicDataSource));
        }
    }

发表评论