我开源的项目JavaMyBatisSpring Boot多租户
2025-05-23 10:33阅读:259评论:0
开源项目-多租户插件druid-multi-tenant-starter
druid-multi-tenant-starter 目标多租户插件,快速集成,开箱即用,支持单个租户以及多个租户,基于alibaba druid 解析SQL,开发过程中只需要像平时一样写SQL语句即可,执行SQL最终会被拦截串改,最终把租户字段添加到SQL语句中,生成新的SQL语句,此过程你无需关
_PROTECTED12_PROTECTED17_PROTECTED16_PROTECTED18_PROTECTED13_
- [x] mybatisinterceptor mybatis拦截器策略,需项目使用的是mybatis ORM框架, 如果项目使用mybatis ORM 框架,推荐使用此方式
jpa__PROTECTED_38__filter 过滤器策略,不管是哪种 ORM框架,只需要数据库连接使用的数据源是 DruidDataSource 即可
_PROTECTED14_PROTECTED19_PROTECTED0_PROTECTED20_PROTECTED1_PROTECTED21_PROTECTED2_PROTECTED22_PROTECTED3_PROTECTED23_PROTECTED4_PROTECTED24_PROTECTED5_PROTECTED25_PROTECTED6_PROTECTED26_PROTECTED7_PROTECTED27_PROTECTED8_PROTECTED28_PROTECTED9_PROTECTED29_PROTECTED10_PROTECTED30_PROTECTED15_PROTECTED31_PROTECTED11__项目中引入以下依赖
1
2
3
4
5
6
<dependency> <groupId>io.github.osinn</groupId> <artifactId>druid-multi-tenant-starter</artifactId> <version>最新版本</version></dependency> _PROTECTED0__
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
--基础配置 1.5.6版本开始 mybatis.tenant.config 改为 multi-tenant.configmulti-tenant: config: # 是否启用多租户插件 enable: true # 数据库表租户ID的列名 tenant-id-column: tenant_id --完整配置参数 multi-tenant: config: # 是否使用MyBatis拦截器方式修改sql enable: true # 数据库表租户ID的列名字段 tenant-id-column: tenant_id # 数据库方言,可选,如果不指定会从事务数据元中自动识别获取方言类型 db-type: mysql # SQL 拦截策略,默认 mybatis 拦截器,如果不是mybatis,且使用的是 DruidDataSource 数据源,推荐使用 druid_filter 过滤器策略 # druid_filter 只支持使用 DruidDataSource 数据数据源连接数据库 sql-interceptor-strategy: mybatis_interceptor # 是否启用切面方式忽略租户ID字段,默认不开启,开启后 @IgnoreTenantIdField 注解不仅仅可以使用在mybatis Mapper接口方法上,也可以使用在Service任意接口上 enable-pointcut-advisor-ignore-tenant-id: false # 是否忽略表按租户ID过滤,默认所有表都按租户ID过滤,指定表名称(区分大小写全等判断) ignore-table-name: - user_role # 内部全等判断 ignore-match-table-alias # 匹配判断指定表别名称是否忽略表按租户ID过滤(区分大小写匹配判断) - temp # 内部判断 以 temp 为前缀的别名进行忽略过滤 # 根据表名前缀判断是否忽略表按租户ID过滤 ignore-table-name-prefix: - act_ # 内部判断 以 act_ 为前缀的别名进行忽略过滤 # 多数据源情况下指定忽略跳过的数据源名称(需要重写ITenantService接口中的ignoreDynamicDatasource方法自行提供获取当前执行的数据源名称) ignore-dynamic-datasource: - demoDataSource # 内部全等判断 # 数据源包装类型, 默认是 null #项目中存在 baomidou(苞米豆) 动态数据源,需要指定 baomidou_dynamic_data_source # # 项目中使用 sleuth 代理 jdbc 同时使用baomidou(苞米豆) 动态数据源,需要指定 sleuth_baomidou_dynamic_data_source_wrapper ,否则需要禁用 sleuth 中的 jdbc 代理 # 如果项目中使用到 sleuth 同时使用baomidou(苞米豆) 动态数据源,应该禁用 sleuth 中的 jdbc 代理,例如以下配置,否则 sql-interceptor-strategy: druid_filter 是不会添加druid过滤器多租户 SQL 解析 # spring: # sleuth: # jdbc: # enabled: false data-source-wrapper-type: baomidou_dynamic_data_source # 是否自定义数据源,默认是 false,true时会调用addCustomDataSourceProxySqlParser()方法,用于自定义数据源添加SQL解析器 enable-custom-data-source: false - 如果SQL中表名不在 ___PROTECTED_13__0___PROTECTED_14__7___PROTECTED_15__1___PROTECTED_16__8___PROTECTED_17__2___PROTECTED_18__9___PROTECTED_19__5___PROTECTED_20__10___PROTECTED_21__3___PROTECTED_22__11___PROTECTED_23__4___PROTECTED_24__12___PROTECTED_25__6__
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/** * 演示:提供多租户ID服务接口 * * @author wency_cai * @since 1.5.6 */@Servicepublic class TenantServiceImpl extends TenantApplicationContext { @Override public List<Object> getTenantIds() { Long tenantId = 1L; List<Object> tenantIdList = new ArrayList<>(); tenantIdList.add(tenantId); return tenantIdList; } } _PROTECTED0__
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** * 演示:提供多租户ID服务接口 * * @author wency_cai * * @since 1.5.6 开始需要继承 TenantApplicationContext 抽象类提供获取多租户ID值 */@Servicepublic class TenantServiceImpl extends TenantApplicationContext { @Override public List<Object> getTenantIds() { // 查询系统多租户id,如果有多个返回多个值即可 int tenantId = 1; return Lists.newArrayList(tenantId); } // 如果需要指定某个数据源跳过设置租户ID,可以重写此方法,并且在yml配置文件中配置 ignore-dynamic-datasource 指定忽略的数据源名称 @Override public String ignoreDynamicDatasource() { // 过去当前执行的数据源名称, 空则需要执行解析设置租户ID return 返回当前执行的数据源名称; } // 如果开发者需要自行解析SQL,可以重新此方法,并返回新的SQL语句,返回值为空时则继续执行默认解析逻辑,有返回值则直接返回且不会执行默认的解析逻辑 @Override public String customizeParser(String originalSQL, Object paramTenantId) { return null; } // 如果开发者想要在执行默认的解析逻辑前做点什么,可以重写此方法,如果有返回值则会覆盖原始SQL语句(originalSQL) @Override public String before(String originalSQL, Object paramTenantId) { System.out.println("执行默认解析逻辑前 执行了 before方法"); return null; } // 如果开发者想要在执行默认的解析逻辑后做点什么,可以重写此方法,如果有返回值则会覆盖解析后的SQL语句(newSQL) @Override public String after(String newSQL, Object paramTenantId) { System.out.println("执行默认解析逻辑后 执行了 after方法");// return newSQL + " and tenant_id = 123456"; return null; } /** * 是否跳过解析,可以重写此方法,(实现当前线程全局判断是否忽略tenant_id字段) * 此方法可以用在此场景,如:init()方法下执行的所有sql语句忽略添加tenant_id字段,可以自行实现 ThreadLocal * 并在此方法中获取当前线程变量判断是否需要忽略添加tenant_id字段,如果需要忽略添加tenant_id字段,则返回true即可 * * @return 返回 true 跳过解析, false 继续解析 */ @Override public boolean skipParser() { return false; } /** * 只有 yml 配置 enable-pointcut-advisor = true 时 内部会使用到 ThreadLocal * 流程: 调用 threadLocalSkipParserSet 方法 设置是否跳过解析 ——> skipParser 从 ThreadLocal 中获取设置值判断 ——> threadLocalSkipParserClear 清理上下文 * 可以重写此方法实现使用自定义 ThreadLocal 实现,对应 调用 ThreadLocal 的 set 方法,即为 myThreadLocal.set(xxxx) * 如果不实现此方法,会调用内部默认的 ThreadLocal 实现 * <p> * 需要注意的是,内部使用的 ThreadLocal 不支持跨线程调用(即 方法上使用@IgnoreTenantIdField 注解,此方法内又有异步方法,在异步方法内操作数据库,此时不会生效,依然会解析设置租户ID) * 如果需要支持跨线程(异步方法),你的框架可参考使用 TransmittableThreadLocal 库创建线程池,然后实现 skipParser、threadLocalSkipParserSet、threadLocalSkipParserClear这三个方法逻辑 * * @since 1.5.6 */ @Override public void threadLocalSkipParserSet() { IGNORE_TENANT.set(Boolean.TRUE); } /** * 只有 yml 配置 enable-pointcut-advisor = true 时 内部会使用到 ThreadLocal * 可以重写此方法实现使用自定义 ThreadLocal 实现,对应 调用 ThreadLocal 的 clear 方法,即为 myThreadLocal.clear() * 如果不实现此方法,会调用内部默认的 ThreadLocal 实现 * * @since 1.5.6 */ @Override public void threadLocalSkipParserClear() { IGNORE_TENANT.remove(); } /** * 预留方法,当bean初始化后调用,自定义数据源添加代理 sql 解析器 * yml 配置 enable-custom-data-source = true 时 才会调用此方法 * * @param dataSource 数据源 * @param defaultSqlParser sql 解析器 * @return 如果需要自定义添加数据源代理 sql 解析器,则返回调整后的 dataSource 数据源,否则返回 null * @since 1.5.6 */ default Object addCustomDataSourceProxySqlParser(Object dataSource, DefaultSqlParser defaultSqlParser) { return null; }} - 到此整合完成
1
2
3
4
5
6
7
8
public interface UserMapper { /** * 传入租户ID值,支持传入List或Set集合 */ User getUserInfoById(@Param("userId") Long userId, @Param("tenant_id") Long tenantId);} - @Param("tenant__PROTECTED_5__id <font color=#FF000 >___PROTECTED_6__2___PROTECTED_7__3___PROTECTED_8__0___PROTECTED_9__4___PROTECTED_10__1__
- mybatis Mapper接口方法上使用
1
2
3
4
5
6
7
8
9
public interface UserMapper { /** * 添加@IgnoreTenantIdField注解来忽略设置多租户字段 */ @IgnoreTenantIdField void deleteTestIgnoreTenantIdById(Long id);} _PROTECTED2_PROTECTED3_PROTECTED0_PROTECTED4_PROTECTED1__注解来忽略设置多租户字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Servicepublic class DemoService { @IgnoreTenantIdField public Role getRole() { return null; }} -- 需要开启 enable-pointcut-advisor-ignore-tenant-id,如下multi-tenant: config: # 需要将此项配置设置为 true enable-pointcut-advisor-ignore-tenant-id: true - 需要注意的是,内部使用的___PROTECTED_15__0___PROTECTED_16__8___PROTECTED_17__1___PROTECTED_18__9___PROTECTED_19__2___PROTECTED_20__10___PROTECTED_21__3___PROTECTED_22__11___PROTECTED_23__4___PROTECTED_24__12___PROTECTED_25__5___PROTECTED_26__13___PROTECTED_27__6___PROTECTED_28__14___PROTECTED_29__7__
sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
SELECT id, name, tenant_idFROM roleWHERE tenant_id = 11------------------------------------- SELECT *FROM user sWHERE s.name = '333' AND s.tenant_id = 11------------------------------------- SELECT tenant_idFROM peopleWHERE id IN ( SELECT id FROM user s WHERE s.tenant_id = 11) AND tenant_id = 11------------------------------------- SELECT tenant_idFROM peopleWHERE id IN ( SELECT id FROM user s WHERE s.tenant_id = 11) AND id IN ( SELECT u.id FROM user u WHERE u.tenant_id = 11) AND tenant_id = 11------------------------------------- SELECT tenant_idFROM peopleWHERE id IN ( SELECT id FROM user s WHERE s.tenant_id = 11) AND id IN ( SELECT u.id FROM user u WHERE u.tenant_id = 11) AND tenant_id IN ( SELECT u.tenant_id FROM user u WHERE u.tenant_id = 11) AND tenant_id = 11------------------------------------- SELECT p.tenant_id , ( SELECT u.name FROM user u WHERE u.id = p.user_id AND u.tenant_id = 11)FROM people pWHERE p.tenant_id = 11------------------------------------- SELECT p.tenant_id , ( SELECT u.name FROM user u WHERE u.id = p.user_id AND u.tenant_id = 11) , ( SELECT s.name FROM user s WHERE s.id = p.user_id AND s.tenant_id = 11)FROM people pWHERE p.tenant_id = 11------------------------------------- SELECT p.tenant_id , ( SELECT u.name , ( SELECT s.name FROM user s WHERE s.id = p.user_id AND s.tenant_id = 11 ) FROM user u WHERE u.id = p.user_id AND u.tenant_id = 11)FROM people pWHERE p.tenant_id = 11------------------------------------- SELECT u.*FROM `user` u JOIN user_role ur ON ur.user_id = u.id AND ur.tenant_id = 11WHERE u.tenant_id = 11------------------------------------- SELECT u.*FROM `user` u JOIN user_role ur ON ur.user_id = u.id AND u.id = 22 AND ur.tenant_id = 11WHERE u.tenant_id = 11------------------------------------- SELECT u.* , ( SELECT ur.name FROM role r WHERE r.id = ur.role_id AND r.tenant_id = 11) AS r_nameFROM `user` u JOIN user_role ur ON ur.user_id = u.id AND u.id = 22 AND ur.tenant_id = 11WHERE u.tenant_id = 11------------------------------------- SELECT u.*FROM `user` u JOIN user_role ur ON ur.user_id = u.id AND ur.role_id IN ( SELECT r.id FROM role r WHERE r.tenant_id = 11 ) AND ur.tenant_id = 11WHERE u.tenant_id = 11------------------------------------- SELECT u.*FROM `user` u JOIN user_role ur ON ur.user_id = u.id AND ur.role_id IN ( SELECT r.id FROM role r WHERE r.tenant_id = 11 ) AND ur.test_id IN ( SELECT r.test_id FROM role r WHERE r.tenant_id = 11 ) AND ur.tenant_id = 11WHERE u.tenant_id = 11------------------------------------- SELECT u.*FROM `user` u LEFT JOIN user_role ur ON ur.user_id = u.id AND ur.tenant_id = 11WHERE u.tenant_id = 11------------------------------------- SELECT u.*FROM `user` u RIGHT JOIN user_role ur ON ur.user_id = u.id AND ur.tenant_id = 11WHERE u.tenant_id = 11------------------------------------- SELECT COUNT(*), idFROM `user`WHERE tenant_id = 11GROUP BY idHAVING COUNT(*) >= 1------------------------------------- SELECT EXISTS ( SELECT * FROM `user` WHERE username = ? AND tenant_id = 11) AS d------------------------------------- SELECT EXISTS ( SELECT tenant_id FROM people WHERE id IN ( SELECT id FROM user s WHERE s.tenant_id = 11 ) AND tenant_id = 11) AS d------------------------------------- SELECT usernameFROM `user`WHERE tenant_id = 11UNIONSELECT usernameFROM accountWHERE tenant_id = 11------------------------------------- SELECT u.*, r.id AS r_id, r.NAME AS r_nameFROM `user` u LEFT JOIN user_role ur ON ur.user_id = u.id AND ur.tenant_id = 11 LEFT JOIN role r ON r.id = ur.role_id AND u.id = 22 AND r.tenant_id = 11WHERE u.tenant_id = 11------------------------------------- SELECT u.*, r.id AS r_id, r.NAME AS r_nameFROM `user` u LEFT JOIN user_role ur ON ur.user_id = u.id AND ur.tenant_id = 11 LEFT JOIN role r ON r.id = ur.role_id AND u.id = 22 AND r.tenant_id = 11 LEFT JOIN menu m ON m.role_id = r.id AND m.tenant_id = 11WHERE u.tenant_id = 11------------------------------------- SELECT a, bFROM ( SELECT * FROM table_a WHERE tenant_id = 11 ) tempWHERE temp.a = 'a';------------------------------------- SELECT temp.*FROM ( SELECT * FROM `user` WHERE tenant_id = 11 ) temp JOIN user_role ur ON ur.user_id = temp.id AND ur.tenant_id = 11WHERE temp.a = 'a' AND temp.tenant_id = 11;------------------------------------- SELECT id, name, tenant_idFROM roleWHERE tenant_id = 1 AND tenant_id = 11 _PROTECTED0__
sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
UPDATE user uSET ds = ?, u.name = ?, id = 'fdf', ddd = ?WHERE id = ? AND u.tenant_id = 11------------------------------------- UPDATE user uSET ds = ?, u.name = ?, id = 'fdf', ddd = ?WHERE id IN (1, 2, 3) AND u.tenant_id = 11------------------------------------- UPDATE user uSET ds = ?, u.name = ?, id = 'fdf', ddd = ?WHERE id IN ( SELECT ur.user_id FROM user_role ur WHERE ur.tenant_id = 11) AND u.tenant_id = 11------------------------------------- UPDATE user uSET ds = ?, u.name = ?, id = 'fdf', ddd = ?WHERE id IN ( SELECT ur.user_id FROM user_role ur WHERE ur.id = 1 AND ur.tenant_id = 11) AND u.tenant_id = 11------------------------------------- UPDATE `user` u JOIN user_role urON ur.user_id = u.id AND u.id = 111 AND ur.tenant_id = 11 SET u.qr_code = '1212'WHERE u.tenant_id = 11------------------------------------- UPDATE `user` u JOIN user_role urON ur.user_id = u.id AND u.id IN ( SELECT urr.user_id FROM user_role urr WHERE urr.tenant_id = 11 ) AND ur.tenant_id = 11 SET u.qr_code = '1212'WHERE u.tenant_id = 11------------------------------------- UPDATE userSET name = CASE WHEN id = 1 THEN 'name1' WHEN id = 2 THEN 'name2' WHEN id = 3 THEN 'name3' ENDWHERE id IN (11, 22, 33, 3) AND tenant_id = 11; _PROTECTED0__
sql
1
2
3
4
5
INSERT INTO `user` (`id`, `username`, `password`, tenant_id)VALUES (?, ?, ?, 11), (?, ?, ?, 11), (?, ?, ?, 11) _PROTECTED0__
sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
DELETE FROM userWHERE id = 1 AND tenant_id = 11------------------------------------- DELETE FROM userWHERE id IN ( SELECT id FROM user s WHERE s.tenant_id = 11) AND tenant_id = 11------------------------------------- DELETE FROM system_permission_data sp LEFT JOIN system_role_data_relation reON sp.id = re.data_id AND re.tenant_id = 11 LEFT JOIN system_role sr ON sr.id = re.role_id AND sr.tenant_id = 11WHERE sr.id = 1 AND sp.tenant_id = 11