Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Example Analysis of SpringBoot Integrated Mybatis-Plus Multi-tenant Architecture

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly shows you the "SpringBoot integrated Mybatis-Plus multi-tenant architecture example analysis", the content is easy to understand, well-organized, hope to help you solve your doubts, the following let the editor lead you to study and learn "SpringBoot integrated Mybatis-Plus multi-tenant architecture example analysis" this article.

one。 What is multi-tenancy?

Multi-tenant technology, or multi-lease technology, referred to as SaaS, is a kind of software architecture technology, which realizes how to share the same system or program components in a multi-user environment (multi-users are generally oriented to enterprise users), and ensures the isolation of data among users. To put it simply: run a single application instance on a server, which provides services to multiple tenants (customers). We can understand from the definition that multi-tenancy is an architecture that aims to use the same set of programs in a multi-user environment and ensure data isolation between users. Then the point is very easy to understand, the focus of multi-tenancy is to achieve multi-user data isolation under the same set of programs.

two。 Multi-tenant architecture and data isolation scheme

There are three main solutions for multi-tenancy in data storage, which are:

1. Independent database

That is, one database per tenant, this scheme has the highest level of user data isolation and the best security, but the cost is high.

Advantages: providing independent databases for different tenants helps to simplify the extended design of the data model to meet the unique needs of different tenants; in the event of failure, it is relatively simple to recover data.

Disadvantages: increasing the number of database installations, resulting in an increase in maintenance and acquisition costs.

two。 Shared database, stand-alone Schema

That is to say, a common database is used for data isolation using tables.

Multiple or all tenants share a Database, but each tenant has a Schema (also known as a user). The underlying libraries, such as DB2, ORACLE, etc., can have multiple SCHEMA in a database.

Advantages: provide a certain degree of logical data isolation, not complete isolation, for tenants with high security requirements; each database can support a larger number of tenants.

Disadvantages: if there is a failure, data recovery is more difficult, because restoring the database will involve the data of other tenants

3. Share database, share Schema, share data table

That is to say, one database and one table are used together for data isolation.

That is, tenants share the same Database, the same Schema, but add the data fields of TenantID multi-tenancy to the table. This is the mode with the highest degree of sharing and the lowest level of isolation.

To put it simply, every time you insert a piece of data, you need to have a customer's identity. Only in this way can we distinguish the data of different customers in the same table, which is currently used in our system (tenant_id).

Advantages: compared with the three schemes, the third scheme has the lowest maintenance and acquisition costs and allows the largest number of tenants supported by each database.

Disadvantages: the lowest isolation level, the lowest security, the need to increase the amount of security development during design and development; data backup and recovery is the most difficult, need to backup and restore table by table.

three。 Where does the multi-tenant architecture apply?

The main consideration of the three models is isolation or sharing.

1. Cost angle factor

The better the isolation, the higher the difficulty and cost of design and implementation, and the higher the initial cost. The better the sharing, the more users are supported under the same operating cost, and the lower the operating cost.

two。 Safety factor

Business and customer security requirements should be considered. The higher the security requirements, the more inclined to isolation.

3. In terms of the number of tenants

The following factors are mainly considered

How many tenants should the system support? Hundreds? Thousands or tens of thousands? The more potential tenants are, the more likely they are to share.

The average amount of space each tenant needs to store data. The more data is stored, the more likely it is to be isolated.

The number of end users accessing the system at the same time for each tenant. The more support you need, the more you tend to be isolated.

Whether you want to provide additional services for each tenant, such as data backup and recovery, etc. The more demand for this, the more likely it is to be isolated.

4. Technical reserve

The higher the sharing, the higher the demand for technology.

four。 Technical realization

Technology selection: Mybatis-Plus

Here we choose the third scheme (shared database, shared Schema, shared data table), which means that each data table needs to have a tenant identity (tenant_id).

There are now database tables (user) as follows:

Field name Field Type description idint (11) Primary key namevarchar (30) name tenant_idint (11) Multi-tenant id

Treat tenant_id as a tenant ID, which is used to isolate the data between tenants. If you want to query the users of the current tenant, the SQL is roughly as follows:

SELECT * FROM user WHERE tenant_id = 1

Just imagine, except for some tables shared by the system, other tenant-related tables, we need to add AND tenant_id =? Query conditions, when there are many data tables, the omission will lead to data leakage.

Thanks to the plug-in mybatis-plus, it is extremely convenient to implement a multi-tenant SQL parser. The official documentation is as follows:

Multi-tenant SQL parser

Officially enter the subject.

Environment building demonstration

1. Create a Spring Boot project

Pom file:

4.0.0 org.springframework.boot spring-boot-starter-parent 2.1.7.RELEASE com.xd mybatis-plus-multi-tenancy 0.0.1-SNAPSHOT mybatis-plus-multi-tenancy Spring Boot Mybatis-Plus-based multi-tenant architecture 1.8 3.1.2 org.springframework.boot Spring-boot-starter-web mysql mysql-connector-java runtime org.projectlombok lombok true com.baomidou mybatis-plus-boot-starter ${mybatis-plus. Version} org.springframework.boot spring-boot-starter-test test junit junit org.springframework spring-test 5.2.0.M1 compile org. Springframework.boot spring-boot-test org.springframework.boot spring-boot-maven-plugin

Application.properties

# data source configuration spring.datasource.type=com.zaxxer.hikari.HikariDataSourcespring.datasource.hikari.minimum-idle=3spring.datasource.hikari.maximum-pool-size=10# cannot be less than 30 seconds, otherwise 1800 seconds spring.datasource.hikari.max-lifetime=30000spring.datasource.hikari.connection-test-query=SELECT 1spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/multi?useUnicode=true&characterEncoding=UTF-8spring.datasource.username=rootspring.datasource.password=rootlogging.level.com.xd.mybatisplusmultitenancy=debug will be returned by default

The corresponding SQL database initializes the schema file

SET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS = 0Mutual-Table structure for user-- DROP TABLE IF EXISTS `user` CREATE TABLE `user` (`id` int (11) NOT NULL AUTO_INCREMENT COMMENT 'primary key', `name` varchar (30) DEFAULT NULL COMMENT 'name', `tenant_ id` int (11) NOT NULL COMMENT 'multi-tenant ID', PRIMARY KEY (`id`) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;SET FOREIGN_KEY_CHECKS = 1

MybatisPlusConfig

Core configuration: TenantSqlParser

Multi-tenant processor

Package com.xd.mybatisplusmultitenancy.config;import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;import lombok.extern.slf4j.Slf4j;import net.sf.jsqlparser.expression.Expression;import net.sf.jsqlparser.expression.LongValue;import net.sf.jsqlparser.expression.NullValue;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import java.util.ArrayList;import java.util.List / * * @ Classname PreTenantHandler * @ Description tenant processor-mainly implements mybatis-plus https://mp.baomidou.com/guide/tenant.html * @ Author Created by Lihaodong (alias: Xiao Dong ah) lihaodongmail@163.com * @ Date 2019-08-09 23:34 * @ Version 1.0 * / @ Slf4j@Componentpublic class MyTenantHandler implements TenantHandler {/ * Multi-tenant identity * / private static final String SYSTEM_TENANT_ID = "tenant_id" / * Table to be filtered * / private static final List IGNORE_TENANT_TABLES = new ArrayList (); @ Autowired private MyContext apiContext; / * tenant Id * * @ return * / @ Override public Expression getTenantId () {/ / extract the currently requested service provider ID from the current system context and inject it into SQL through the parser. Long tenantId = apiContext.getCurrentTenantId (); log.debug ("current tenant is {}", tenantId); if (tenantId = = null) {return new NullValue ();} return new LongValue (tenantId);} / * * tenant field name * * @ return * / @ Override public String getTenantIdColumn () {return SYSTEM_TENANT_ID } / * determine whether to filter according to the table name * ignore some tables: for example, the tenant table (sys_tenant) does not need to perform such processing * * @ param tableName * @ return * / @ Override public boolean doTableFilter (String tableName) {return IGNORE_TENANT_TABLES.stream (). AnyMatch ((e)-> e.equalsIgnoreCase (tableName));}}

Configuration of MybatisPlus

Package com.xd.mybatisplusmultitenancy.config;import com.baomidou.mybatisplus.core.parser.ISqlParser;import com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser;import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;import com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor;import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;import org.mybatis.spring.annotation.MapperScan;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.expression.LongValue;import java.util.ArrayList;import java.util.List;/** * @ Classname MybatisPlusConfig * @ Description TODO * @ Author Created by Lihaodong (alias: Xiao Dong) lihaodongmail@163.com * @ Date 2019-08-09 22:44 * @ Version 1.0 * / @ Configuration@MapperScan ("com.xd.mybatisplusmultitenancy.mapper") public class MybatisPlusConfig {@ Autowired private MyTenantHandler myTenantHandler @ Bean public PaginationInterceptor paginationInterceptor () {PaginationInterceptor paginationInterceptor = new PaginationInterceptor (); / / SQL parsing processing interception: add tenants to handle callbacks. List sqlParserList = new ArrayList (); / / attack SQL to block the parser and add the resolution chain sqlParserList.add (new BlockAttackSqlParser ()); / / Multi-tenant interception TenantSqlParser tenantSqlParser = new TenantSqlParser (); tenantSqlParser.setTenantHandler (myTenantHandler); sqlParserList.add (tenantSqlParser); paginationInterceptor.setSqlParserList (sqlParserList); return paginationInterceptor } / * performance analysis interceptor, not recommended for production * to observe SQL execution and execution duration * / @ Bean (name = "performanceInterceptor") public PerformanceInterceptor performanceInterceptor () {return new PerformanceInterceptor ();}}

Customize the context of the system

Package com.xd.mybatisplusmultitenancy.config;import org.springframework.stereotype.Component;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;/** * @ Classname ApiContext * @ Description context of the current system * @ Author Created by Lihaodong (alias: Xiao Dong) lihaodongmail@163.com * @ Date 2019-08-09 22:47 * @ Version 1.0 * / @ Componentpublic class MyContext {private static final String KEY_CURRENT_TENANT_ID = "KEY_CURRENT_PROVIDER_ID" Private static final Map M_CONTEXT = new ConcurrentHashMap (); public void setCurrentTenantId (Long tenantId) {M_CONTEXT.put (KEY_CURRENT_TENANT_ID, tenantId);} public Long getCurrentTenantId () {return (Long) M_CONTEXT.get (KEY_CURRENT_TENANT_ID);}}

Entity, Mapper are omitted.

two。 Unit test package com.xd.mybatisplusmultitenancy.test;import com.baomidou.mybatisplus.core.toolkit.Wrappers;import com.xd.mybatisplusmultitenancy.MybatisPlusMultiTenancyApplication;import com.xd.mybatisplusmultitenancy.config.MyContext;import com.xd.mybatisplusmultitenancy.entity.User;import com.xd.mybatisplusmultitenancy.mapper.UserMapper;import lombok.extern.slf4j.Slf4j;import org.junit.Assert;import org.junit.Before;import org.junit.FixMethodOrder;import org.junit.Test;import org.junit.runner.RunWith;import org.junit.runners.MethodSorters Import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.sql.Wrapper / * @ Classname MybatisPlusMultiTenancyApplicationTests * @ Description TODO * @ Author Created by Lihaodong (alias: Xiao Dong) lihaodongmail@163.com * @ Date 2019-08-09 22:50 * @ Version 1.0 * / @ Slf4j@RunWith (SpringRunner.class) @ FixMethodOrder (MethodSorters.JVM) @ SpringBootTest (classes = MybatisPlusMultiTenancyApplication.class) public class MybatisPlusMultiTenancyApplicationTests {@ Autowired private MyContext apiContext; @ Autowired private UserMapper userMapper / * simulate the current system's multi-tenant Id * / @ Before public void before () {/ / set the current multi-tenant id apiContext.setCurrentTenantId (1L) in context;} @ Test public void insert () {/ / add data User user = new User () .setName ("Xiaoming") / / determine whether a condition is true or false Assert.assertTrue (userMapper.insert (user) > 0); user = userMapper.selectById (user.getId ()); log.info ("insert data: {}", user); / / determine whether it is equal to Assert.assertEquals (apiContext.getCurrentTenantId (), user.getTenantId ()) } @ Test public void selectList () {userMapper.selectList (null) .forEach ((e)-> {log.info ("query data {}", e); Assert.assertEquals (apiContext.getCurrentTenantId (), e.getTenantId ());});}}

Running result

Insert data

2019-08-23 22 o.s.s.concurrent.ThreadPoolTaskExecutor 32 INFO 52.755 77902-[main] o.s.s.concurrent.ThreadPoolTaskExecutor: Initializing ExecutorService 'applicationTaskExecutor'

2019-08-23 22 Started MybatisPlusMultiTenancyApplicationTests in 32 seconds 53.210 INFO 77902-[main] .MybatisPlusMultiTenancyApplicationTests: MybatisPlusMultiTenancyApplicationTests (JVM running for 5.181)

2019-08-23 22 DEBUG 32 DEBUG 77902-[main] c.x.m.config.MyTenantHandler: current tenant is 1

2019-08-23 22 c.x.m.mapper.UserMapper.insert 32 DEBUG 77902-[main] c.x.m.mapper.UserMapper.insert: = > Preparing: INSERT INTO user (name, tenant_id) VALUES (?, 1)

2019-08-23 22 DEBUG 32 DEBUG 77902-[main] c.x.m.mapper.UserMapper.insert: = > Parameters: Xiao Ming (String)

2019-08-23 22 c.x.m.mapper.UserMapper.insert 32 DEBUG 77902-[main] c.x.m.mapper.UserMapper.insert: Preparing: SELECT id, name, tenant_id FROM user WHERE user.tenant_id = 1 AND id =?

2019-08-23 22 c.x.m.mapper.UserMapper.selectById 32 DEBUG 77902-[main] c.x.m.mapper.UserMapper.selectById: = > Parameters: 1 (Long)

2019-08-23 22 DEBUG 32 DEBUG 77902-[main] c.x.m.mapper.UserMapper.selectById: Preparing: SELECT id, name, tenant_id FROM user WHERE user.tenant_id = 1

2019-08-23 22 DEBUG 3414 27.442 DEBUG 77922-[main] c.x.m.mapper.UserMapper.selectList: = > Parameters:

2019-08-23 22 c.x.m.mapper.UserMapper.selectList 3415 27.464 DEBUG 77922-[main]

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report