The code has been uploaded to the code cloud: gitee.com/lezaiclub/s… Welcome to the white piao

The introduction

Today we are going to talk about multi-tenant. In fact, multi-tenant is mainly about data isolation. That is, each enterprise or user has its own independent data and does not mix with other people’s data. Multi-tenancy can be implemented in three main ways:

Standalone database

This way the most simple and clear, every enterprise or the user on the platform by an independent database to isolate their own data, it is the physical isolation, achieves the data which is its advantages, but his disadvantage is that for each of the enterprise or the user to create a separate database, the cost is very large, and space utilization rate is not high also, cause serious waste. Summary:

  • Advantages: Complete data isolation and high security
  • Disadvantages: high cost, database, difficult to maintain

Same database, different tables

This method is logically isolated. The data of different users are stored in the same database, but different tables are used to store the data of different users to achieve data isolation. Compared with the above method, the cost is reduced and the data isolation is also achieved

Same database, same table, differentiated by field

In this way, compared with the above two methods, the cost is less. Different data can be distinguished only by fields. This way is simple to maintain and cost less, but it is a big trouble to export and migrate data

  • Advantages: Convenient maintenance, low cost, and simple implementation. A large number of tenants can be maintained
  • Disadvantages: Data is easy to migrate, and data is not completely isolated

By comparing the above three methods, we have made clear the differences of each implementation scheme and their advantages and disadvantages. In this paper, we will implement the third method to achieve multi-tenancy by integrating mybatisPlus.

Environment set up

Based on the environment in the previous section, we have built an environment that integrates mybatisPlus. Create a new member table (tenant_id) to hold tenant information. Create a new member table (tenant_id) to hold tenant information

ALTER TABLE  `member` 
ADD COLUMN `tenant_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '租户id' AFTER `member_level`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`id`) USING BTREE;
Copy the code

coding

Add a request context helper class

This class stores information about the current requesting user, implemented using threadLocal, and bound to the current requesting thread

package com.aims.mybatisplus.conf; public class TenantRequestContext { private static ThreadLocal<String> tenantLocal = new ThreadLocal<>(); public static void setTenantLocal(String tenantId) { tenantLocal.set(tenantId); } public static String getTenantLocal() { return tenantLocal.get(); } public static void remove() { tenantLocal.remove(); }}Copy the code

Add an authentication interceptor

The interceptor basically takes the tenant ID from the request header and puts it in context for mybatisPlus to get

package com.aims.mybatisplus.interceptor; import com.aims.mybatisplus.conf.TenantRequestContext; import com.mysql.cj.util.StringUtils; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String userId = request.getHeader("tenant_id"); if (! StringUtils.isNullOrEmpty(userId)) { TenantRequestContext.setTenantLocal(userId); System.out.printf(" current tenant ID:"+userId); } return HandlerInterceptor.super.preHandle(request, response, handler); }}Copy the code

Configuring interceptors

package com.aims.mybatisplus.interceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class LoginConfig implements WebMvcConfigurer { @Override public void AddInterceptors (InterceptorRegistry registry) {// InterceptorRegistration = registry.addInterceptor(new AuthInterceptor()); registration.addPathPatterns("/**"); . / / all paths are intercepted registration excludePathPatterns (/ / add not intercept path "your landing path", / / login "/ / *. * * HTML", / / HTML static resource "/ * * / *. Js", / / js static resource "/ * * / *. CSS", / / CSS static resource ". / / * * * woff ", "/ / * * *. The vera.ttf"); }}Copy the code

Add the mybatisPlus configuration class

This class is used to configure the mybatisPlus interceptor, which is used to configure the tenant ID field, which table can obtain the tenant processing, the tenant ID from the context

package com.aims.mybatisplus.conf; import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; import net.sf.jsqlparser.expression.LongValue; import net.sf.jsqlparser.expression.StringValue; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.schema.Column; @Configuration public class MybatisConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() { @Override public Expression GetTenantId () {/ / get the return from the context of new StringValue (TenantRequestContext. GetTenantLocal ()); @override public Boolean ignoreTable(String tableName) {return!" member".equalsIgnoreCase(tableName); @override public String getTenantIdColumn() {return "tenant_id"; }})); return interceptor; }}Copy the code

Current directory structure

Writing test interfaces

Note that the tenant information needs to be in the request header,

@RestController public class TenantController { @Autowired private MemberMapper memberMapper; @requestMapping ("/testTenant") public String testTenantId() {Member Member = new Member(); Member.setmembername (" test tenant ID"); memberMapper.insert(member); return "success"; @requestMapping ("/getCurrentTenantMember") public List<Member> getCurrentTenantMember() { QueryWrapper<Member> queryWrapper = new QueryWrapper<>(); return memberMapper.selectList(queryWrapper); }}Copy the code

The last

Through the above demonstration, I believe that you should have achieved multiple groups of users, there will be more mybatisPlus combat tutorial to share with you