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");
通过上述方法,可以在程序运行时对租户编码进行切换。切换时需注意以下两点:
- 在进入事务前进行切换,进入事务后无法切换;
- 切换只对当前线程有效;
- 在开启事务的方法开头使用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");
}
}