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