MultiTenantDataSourceProvider

MultiTenantDataSourceProvider是配置静态多数据源用的。需要在配置文件里添加静态数据源及指定hibernate的多租户配置
配置示例:

spring:
    jpa:
        properties:
            hibernate.multiTenancy: SCHEMA
            hibernate.tenant_identifier_resolver: com.thsware.framework.multitenancy.MultiTenantIdentifierResolver
            hibernate.multi_tenant_connection_provider: com.thsware.framework.multitenancy.MultiTenantConnectionProviderImpl
thsware:
    multi-tenant:
        enable: true
        datasource:
            - name: default
              url: jdbc:mysql://192.168.0.50:9306/cscgg?useUnicode=true&characterEncoding=utf8&useSSL=false
              username: root
              password: root
            - name: cscgg2
              url: jdbc:mysql://192.168.0.50:9306/cscgg2?useUnicode=true&characterEncoding=utf8&useSSL=false
              username: root
              password: root

可通过MultiTenantDataSourceProvider的addDataSource()和removeDataSource()方法在程序运行中动态增删数据源

TenantCodeResolver

TenantCodeResolver是搭配租户平台(ths-saas项目)使用的,在租户平台中管理数据源。
配置示例:

spring:
    jpa:
        properties:
            hibernate.multiTenancy: SCHEMA
            hibernate.tenant_identifier_resolver: com.thsware.framework.multitenancy.TenantCodeResolver
            hibernate.multi_tenant_connection_provider: com.thsware.framework.multitenancy.MultiTenantConnectionProviderImpl
thsware:
    saas:
        enable: true
        platformUrl: http://localhost:8088 #租户平台地址
        businessDatabase: thsuaa #业务对应数据库名
        database:
            url: jdbc:mysql://192.168.0.50:3306/ThsSaas?useUnicode=true&characterEncoding=utf8&useSSL=false
            username: root
            password: root
        sycnRate: 1800 #租户平台数据源同步频率(单位秒)
        useReqHeaderTenantUrl: demo/url1,deom/url2 #优先使用请求头header中的租户编码的url(key:Ths-Tenant)

租户id手动切换

MultiTenantDataSourceProvider.setCustomTenant("test");

通过上述方法,可以在程序运行时对租户编码进行切换。切换时需注意以下两点:

  1. 在进入事务前进行切换,进入事务后无法切换;
  2. 切换只对当前线程有效;
  3. 在开启事务的方法开头使用ApplicationEventPublisher的方法publishEvent(new CustomTenantEvent(this))发布消息,可确保在事务完成/回滚时自动清除当前手动设置的租户id,避免影响同一请求的后续数据库操作仍使用手动设置的租户id;

默认所有service类都带有@Transactional(readOnly = true)注解,该注解表示类的每个方法会开启或加入事务。
所以在切换租户id时,需确保所在的方法不在事务中,可通过@Transactional(propagation = Propagation.NOT_SUPPORTED)标记方法不参与事务。
其次,同一个类中进行调用同类的带有事务注解方法时,需通过获取spring aop代理的实例进行调用,否则调用的方法的事务注解不生效。

完整的demo如下:


@Service
@Transactional(readOnly = true)
@EnableAspectJAutoProxy(exposeProxy = true)
public class DemoService{
    @Autowired
    private ApplicationEventPublisher publisher;
    @Autowired
    private DemoRespository demoRespository;

    @Transactional(propagation  = Propagation.NOT_SUPPORTED)
    public void demo(){
        //获取spring aop代理的当前类的实例
        DemoService proxyService = (DemoService) AopContext.currentProxy();
        //手动设置租户编码为test
        MultiTenantDataSourceProvider.setCustomTenant("test");
        //下面的查询demo1使用test租户的库,demo2使用默认或者根据token解析的租户的库
        Demo demo1 = proxyService.query1();
        Demo demo2 = proxyService.query2();    //demo1 != demo2
         
        MultiTenantDataSourceProvider.setCustomTenant("test");
        //下面的两个查询会使用同一个test租户的库
        Demo demo3 = proxyService.query2();
        Demo demo4 = proxyService.query1();    //demo3 == demo4
    }
   
    @Transactional(readOnly = true)
    public Demo query1(){
        //标记事务完成后自动调用MultiTenantDataSourceProvider.cleanCustomTenant()清除手动设置的租户编码
        publisher.publishEvent(new CustomTenantEvent(this));
        return demoRespository.getOne("id");
    }
    
    @Transactional(readOnly = true)
    public Demo query2(){
        //没有自动清除手动设置的租户编码
        
        return demoRespository.getOne("id");
    }
    
}
最后修改:2023 年 04 月 19 日
如果觉得我的文章对你有用,请随意赞赏