多数据源切换问题

本文介绍了在Spring Boot应用中如何处理多数据源切换问题,特别是在处理租户数据时。通过创建独立的租户信息库,利用Hibernate 5的动态数据源特性,结合ThreadLocal保存租户ID,实现数据源的动态配置。在用户请求时,根据租户ID选择相应数据源,确保数据隔离。同时,文章提到了使用拦截器和AbstractRoutingDataSource进行数据源的选择和路由。
摘要由CSDN通过智能技术生成

维护、识别和路由租户数据源

我们可以提供一个独立的库来存放租户信息,如数据库名称、链接地址、用户名、密码等,这可以统一的解决租户信息维护的问题。租户的识别和路由有很多种方法可以解决,下面列举几个常用的方式:
在这里插入图片描述

我们都知道,在启动Spring Boot应用程序之前,就需要为其提供有关数据源的配置信息(有使用到数据库的情况下),按照一开始的需求,有N个客户需要使用我们的应用程序,我们就需要提前配置好N个数据源(多数据源),如果N<50,我认为我还能忍受,如果更多,这样显然是无法接受的。为了解决这一问题,我们需要借助Hibernate 5提供的动态数据源特性,让我们的应用程序具备动态配置客户端数据源的能力。简单来说,当用户请求系统资源时,我们将用户提供的租户信息(tenantId)存放在ThreadLoacal中,紧接着获取TheadLocal中的租户信息,并根据此信息查询单独的租户库,获取当前租户的数据配置信息,然后借助Hibernate动态配置数据源的能力,为当前请求设置数据源,最后之前用户的请求。这样我们就只需要在应用程序中维护一份数据源配置信息(租户数据库配置库),其余的数据源动态查询配置。接下来,我们将快速的演示这一功能。
解决方案:可以让付费用户自己使用一个datasource, 让体验用户都集中在一个datasource中

用户库

在这里插入图片描述
两个不同的用户库


@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {
   

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
   
        // 可添加多个
        registry.addInterceptor(new ChangeDataSourceInterceptor()).addPathPatterns("/test/**");
    }
}

@Slf4j
public class ChangeDataSourceInterceptor implements HandlerInterceptor {
   

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
   
        Long tenantId = Convert.toLong(request.getHeader("tenant_id"));
        System.out.println(tenantId);

        DataSourceContextHolder.setDataSource(String.valueOf(tenantId));

        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
}

代码解析:通过拦截器的设置,可以在请求进来的时候就进行数据源的选择

public class DataSourceContextHolder {
   
    //此类提供线程局部变量。这些变量不同于它们的正常对应关系是每个线程访问一个线程(通过get、set方法),有自己的独立初始化变量的副本。
    private static final ThreadLocal<String> DATASOURCE_HOLDER = new ThreadLocal<>();

    /**
     * 设置数据源
     * @param dataSourceName 数据源名称
     */
    public static void setDataSource(String dataSourceName){
   
        DATASOURCE_HOLDER.set(dataSourceName);
    }

    /**
     * 获取当前线程的数据源
     * @return 数据源名称
     */
    public static String getDataSource(){
   
        return DATASOURCE_HOLDER.get();
    }

    /**
     * 删除当前数据源
     */
    public static void removeDataSource(){
   
        DATASOURCE_HOLDER.remove();
    }

}

通过threadLocal来进行key的保存,用于后续选择数据源

@Configuration
@Slf4j
public class DruidDBConfig {
   

    @Value("${spring.datasource.url}")
    private String dbUrl;
    @Value("${spring.datasource.username}")
    private String username;
    @Value("${spring.datasource.password}")
    private String password;
    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;
    // 连接池连接信息
    @Value("${spring.datasource.initial-size}")
    private int initialSize;
    @Value("${spring.datasource.min-idle}")
    private int minIdle;
    @Value("${spring.datasource.max-active}")
    private
  • 26
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值