MybatisPlus多租户插件的使用

MybatisPlus多租户插件的使用

实现过程描述

实现过程可以总结为:

实现TenantLineHandler接口: KevinQTenantLineHandler定义一个类TenantContextHolder,通过TheadLocal存储与获取当前请求线程的租户编号定义Web过滤器TenantContextWebFilter,拦截请求,获取header中的租户编号,并通过步骤2的类来设置值通过MybatisPlus的接口,添加拦截器:interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new KevinQTenantLineHandler()));具体实现

MybatisPlus官方文档列了一个接口,显然,我们需要实现该接口:

/** * @author qww * 2023/4/11 18:27 */public class KevinQTenantLineHandler implements TenantLineHandler { /** * 获取当前执行上下文中的tenant-id * @return */ @Override public Expression getTenantId() { return new LongValue(TenantContextHolder.getRequiredTenantId()); } /** * 租户id的列名 * @return */ @Override public String getTenantIdColumn() { return "tenant_id"; } /** * 是否忽略某些表 * @param tableName * @return */ @Override public boolean ignoreTable(String tableName) { return false; } }复制代码

上述代码中有一个类TenantContextHolder,这个类是一个通过ThreadLocal变量来存储租户ID的类,具体实现如下:

/** * 多租户上下文 Holder */ public class TenantContextHolder { /** * 当前租户编号 */ private static final ThreadLocal TENANT_ID = new ThreadLocal(); /** * 是否忽略租户 */ private static final ThreadLocal IGNORE = new ThreadLocal(); /** * 获得租户编号。 * * @return 租户编号 */ public static Long getTenantId() { return TENANT_ID.get(); } /** * 获得租户编号。如果不存在,则抛出 NullPointerException 异常 * * @return 租户编号 */ public static Long getRequiredTenantId() { Long tenantId = getTenantId(); if (tenantId == null) { throw new NullPointerException("TenantContextHolder 不存在租户编号"); } return tenantId; } public static void setTenantId(Long tenantId) { TENANT_ID.set(tenantId); } public static void setIgnore(Boolean ignore) { IGNORE.set(ignore); } /** * 当前是否忽略租户 * * @return 是否忽略 */ public static boolean isIgnore() { return Boolean.TRUE.equals(IGNORE.get()); } public static void clear() { TENANT_ID.remove(); IGNORE.remove(); } }复制代码

其中通过setTenantId与getTenantId分别来设置与获取当下线程中的租户ID。

另外我们还需要通过一个filter来获取前端页面上传过来的租户ID值:

/** * 多租户 Context Web 过滤器 */ @Component("tenantContextWebFilter") public class TenantContextWebFilter extends OncePerRequestFilter { public static final String HEADER_TENANT_ID = "tanent-id"; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { // 设置 Long tenantId = getTenantId(request); if (tenantId != null) { TenantContextHolder.setTenantId(tenantId); } try { chain.doFilter(request, response); } finally { // 清理 TenantContextHolder.clear(); } } /** * 获得租户编号,从 header 中 * 考虑到其它 framework 组件也会使用到租户编号,所以不得不放在 WebFrameworkUtils 统一提供 * * @param request 请求 * @return 租户编号 */ public static Long getTenantId(HttpServletRequest request) { String tenantId = request.getHeader(HEADER_TENANT_ID); return StrUtil.isNotEmpty(tenantId) ? Long.valueOf(tenantId) : null; } }复制代码

最后,我们需要给MybatisPlus添加拦截器,该拦截器使用我们实现的接口:

/** * mybatis configuration */ @ConditionalOnClass({MybatisPlusAutoConfiguration.class, SqlSessionFactory.class, SqlSessionFactoryBean.class}) @Configuration public class KevinQMybatisAutoConfiguration { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(ObjectProvider customizerObjectProvider) { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 添加拦截器,注意多个拦截器的添加顺序 interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new KevinQTenantLineHandler())); return interceptor; }}复制代码

此时,若我们不对前端做修改,则会报错:租户编号不存在。

需要我们前端的小伙伴在请求的header部分添加tenant-id=xx。

后端一方面是需要在数据库中需要分租户的表添加字段tenant_id,并且需要在代码中配置哪些表需要忽略租户ID:tenant-id。

遗留的一些问题可能有一些接口也需要忽略租户ID,如基础的校验码获取等,需要配置与修改如何快速配置忽略的数据库table表,硬改代码不是我们的风格。如果涉及到定时任务,redis等,又该如何区分租户呢?

比丘资源网 » MybatisPlus多租户插件的使用

发表回复

提供最优质的资源集合

立即查看 了解详情