提交 | 用户 | 时间
|
d45017
|
1 |
package com.iailab.framework.tenant.core.db.dynamic; |
潘 |
2 |
|
|
3 |
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; |
|
4 |
import com.baomidou.dynamic.datasource.creator.DataSourceProperty; |
|
5 |
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator; |
|
6 |
import com.baomidou.dynamic.datasource.processor.DsProcessor; |
441d30
|
7 |
import com.iailab.framework.tenant.core.context.DataContextHolder; |
d45017
|
8 |
import com.iailab.framework.tenant.core.context.TenantContextHolder; |
潘 |
9 |
import com.iailab.framework.tenant.core.service.TenantFrameworkService; |
441d30
|
10 |
import com.iailab.module.infra.api.db.DataSourceConfigServiceApi; |
J |
11 |
import com.iailab.module.infra.api.db.dto.DataSourceConfigRespDTO; |
d45017
|
12 |
import lombok.RequiredArgsConstructor; |
潘 |
13 |
import org.aopalliance.intercept.MethodInvocation; |
|
14 |
import org.springframework.context.annotation.Lazy; |
|
15 |
|
|
16 |
import javax.annotation.Resource; |
|
17 |
import javax.sql.DataSource; |
|
18 |
import java.util.Objects; |
|
19 |
|
|
20 |
/** |
|
21 |
* 基于 {@link TenantDS} 的数据源处理器 |
|
22 |
* |
|
23 |
* 1. 如果有 @TenantDS 注解,返回该租户的数据源 |
|
24 |
* 2. 如果该租户的数据源未创建,则进行创建 |
|
25 |
* |
|
26 |
* @author 芋道源码 |
|
27 |
*/ |
|
28 |
@RequiredArgsConstructor |
|
29 |
public class TenantDsProcessor extends DsProcessor { |
|
30 |
|
|
31 |
/** |
|
32 |
* 用于获取租户数据源配置的 Service |
|
33 |
*/ |
|
34 |
@Resource |
|
35 |
@Lazy |
|
36 |
private TenantFrameworkService tenantFrameworkService; |
|
37 |
|
|
38 |
/** |
|
39 |
* 动态数据源 |
|
40 |
*/ |
|
41 |
@Resource |
|
42 |
@Lazy // 为什么添加 @Lazy 注解?因为它和 DynamicRoutingDataSource 相互依赖,导致无法初始化 |
|
43 |
private DynamicRoutingDataSource dynamicRoutingDataSource; |
|
44 |
|
|
45 |
/** |
|
46 |
* 用于创建租户数据源的 Creator |
|
47 |
*/ |
|
48 |
@Resource |
|
49 |
@Lazy |
|
50 |
private DefaultDataSourceCreator dataSourceCreator; |
|
51 |
|
441d30
|
52 |
@Resource |
J |
53 |
@Lazy |
|
54 |
private DataSourceConfigServiceApi dataSourceConfigServiceApi; |
|
55 |
|
d45017
|
56 |
@Override |
潘 |
57 |
public boolean matches(String key) { |
441d30
|
58 |
return Objects.equals(key, TenantDS.KEY) || Objects.equals(key, DataDS.KEY); |
d45017
|
59 |
} |
潘 |
60 |
|
|
61 |
@Override |
|
62 |
public String doDetermineDatasource(MethodInvocation invocation, String key) { |
441d30
|
63 |
if (DataDS.KEY.equals(key)){ |
J |
64 |
// 获得数据源配置 |
|
65 |
Long dataSourceId = DataContextHolder.getRequiredDataSourceId(); |
|
66 |
DataSourceConfigRespDTO dataSourceConfigRespDTO = dataSourceConfigServiceApi.getDataSourceConfig(dataSourceId); |
|
67 |
DataSourceProperty dataSourceProperty = new DataSourceProperty(); |
|
68 |
dataSourceProperty.setPoolName(dataSourceConfigRespDTO.getName()); |
|
69 |
dataSourceProperty.setUrl(dataSourceConfigRespDTO.getUrl()); |
|
70 |
dataSourceProperty.setUsername(dataSourceConfigRespDTO.getUsername()); |
|
71 |
dataSourceProperty.setPassword(dataSourceConfigRespDTO.getPassword()); |
|
72 |
// 创建 or 创建数据源,并返回数据源名字 |
|
73 |
return createDatasourceIfAbsent(dataSourceProperty); |
|
74 |
}else if(TenantDS.KEY.equals(key)){ |
|
75 |
// 获得数据源配置 |
|
76 |
Long tenantId = TenantContextHolder.getRequiredTenantId(); |
|
77 |
DataSourceProperty dataSourceProperty = tenantFrameworkService.getDataSourceProperty(tenantId); |
|
78 |
// 创建 or 创建数据源,并返回数据源名字 |
|
79 |
return createDatasourceIfAbsent(dataSourceProperty); |
|
80 |
} |
|
81 |
return key; |
d45017
|
82 |
} |
潘 |
83 |
|
|
84 |
private String createDatasourceIfAbsent(DataSourceProperty dataSourceProperty) { |
|
85 |
// 1. 重点:如果数据源不存在,则进行创建 |
|
86 |
if (isDataSourceNotExist(dataSourceProperty)) { |
|
87 |
// 问题一:为什么要加锁?因为,如果多个线程同时执行到这里,会导致多次创建数据源 |
|
88 |
// 问题二:为什么要使用 poolName 加锁?保证多个不同的 poolName 可以并发创建数据源 |
|
89 |
// 问题三:为什么要使用 intern 方法?因为,intern 方法,会返回一个字符串的常量池中的引用 |
|
90 |
// intern 的说明,可见 https://www.cnblogs.com/xrq730/p/6662232.html 文章 |
|
91 |
synchronized (dataSourceProperty.getPoolName().intern()) { |
|
92 |
if (isDataSourceNotExist(dataSourceProperty)) { |
|
93 |
DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty); |
|
94 |
dynamicRoutingDataSource.addDataSource(dataSourceProperty.getPoolName(), dataSource); |
|
95 |
} |
|
96 |
} |
|
97 |
} |
|
98 |
// 2. 返回数据源的名字 |
|
99 |
return dataSourceProperty.getPoolName(); |
|
100 |
} |
|
101 |
|
|
102 |
private boolean isDataSourceNotExist(DataSourceProperty dataSourceProperty) { |
|
103 |
return !dynamicRoutingDataSource.getDataSources().containsKey(dataSourceProperty.getPoolName()); |
|
104 |
} |
|
105 |
|
|
106 |
} |