最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MyBatisPlus實現(xiàn)多租戶數(shù)據(jù)隔離

 更新時間:2026年06月21日 10:59:43   作者:yifanghub  
多租戶是一個常見的架構(gòu)需求,特別是在 SaaS應(yīng)用中,其核心目標(biāo)是在單個應(yīng)用實例中為多個租戶提供服務(wù),同時確保他們的數(shù)據(jù)、配置和用戶體驗是隔離的,本文就來詳細(xì)的介紹一下多租戶數(shù)據(jù)隔離,需要的朋友可以參考下

多租戶(Multi-tenancy)是一個常見的架構(gòu)需求,特別是在 SaaS應(yīng)用中。其核心目標(biāo)是在單個應(yīng)用實例中為多個租戶(客戶)提供服務(wù),同時確保他們的數(shù)據(jù)、配置和用戶體驗是隔離的。

一、多租戶的常見的三種模式

  1. 獨立數(shù)據(jù)庫,這是隔離級別最高、最安全的方案,為每個租戶創(chuàng)建獨立的、物理上隔離的數(shù)據(jù)庫
  2. 共享數(shù)據(jù)庫,獨立 Schema,在同一個數(shù)據(jù)庫實例中,為每個租戶創(chuàng)建獨立的 Schema,所有租戶共享一個數(shù)據(jù)庫實例,但每個租戶擁有自己的一套表結(jié)構(gòu)(Schema)
  3. 共享數(shù)據(jù)庫,共享 Schema,所有租戶共享同一個數(shù)據(jù)庫實例和同一套表結(jié)構(gòu)。通過在每張業(yè)務(wù)表中增加一個 tenant_id 字段來區(qū)分不同租戶的數(shù)據(jù)。這是最經(jīng)濟(jì)、資源利用率最高的方案,也是最常見的 SaaS 多租戶模式

今天我們介紹是第三種方案——在同一個數(shù)據(jù)庫的同一張表中,通過tenant_id字段實現(xiàn)數(shù)據(jù)隔離。

二、MyBatisPlus多租戶原理解析

核心思想:SQL自動改寫

MyBatisPlus通過攔截器機(jī)制,在SQL執(zhí)行前自動加上租戶條件:

// 你寫的SQL:
SELECT * FROM sys_user WHERE status = 1;
// MyBatisPlus自動改寫的SQL:  
SELECT * FROM sys_user WHERE status = 1 AND tenant_id = 'T001';

關(guān)鍵技術(shù)點

  • TenantLineHandler:租戶處理器,決定租戶值怎么取、哪些表要過濾等
  • TenantLineInnerInterceptor:多租戶攔截器,SQL攔截和改寫核心邏輯
  • Ignore注解:標(biāo)記不需要自動添加租戶條件的方法

三、案例

環(huán)境準(zhǔn)備

pom.xml依賴:

<dependencies>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.3.1</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
    <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-extension</artifactId>
            <version>3.5.3</version>
        </dependency>
</dependencies>

數(shù)據(jù)庫表結(jié)構(gòu):

CREATE TABLE orders
(
    id           BIGINT PRIMARY KEY AUTO_INCREMENT,
    order_no     VARCHAR(64)    NOT NULL,
    amount       DECIMAL(10, 2) NOT NULL,
    tenant_id    VARCHAR(32)    NOT NULL, -- 租戶標(biāo)識字段
    created_time DATETIME DEFAULT CURRENT_TIMESTAMP
);

核心代碼實現(xiàn)

配置文件

server:
  port: 8080
spring:
  application:
    name: multitenant
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://xxx:3306/test1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
    username: admin
    password: 123456
  sql:
    init:
      schema-locations: classpath:db/init.sql
      mode: always
mybatis:
  mapper-locations: classpath:/mapper/*.xml
logging:
  level:
    com:
      example: debug

租戶上下文管理:

/**
 * 租戶上下文:用于在同一個線程內(nèi)傳遞租戶信息
 */
public class TenantContext {
    private static final ThreadLocal<String> CURRENT_TENANT = new ThreadLocal<>();

    public static void setTenantId(String tenantId) {
        CURRENT_TENANT.set(tenantId);
    }

    public static String getTenantId() {
        return CURRENT_TENANT.get();
    }

    public static void clear() {
        CURRENT_TENANT.remove();
    }
}

MyBatisPlus多租戶配置:

@Configuration
public class MybatisPlusConfig {
    /**
     * 多租戶攔截器
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        // 創(chuàng)建租戶攔截器實例
        TenantLineInnerInterceptor tenantInterceptor = new TenantLineInnerInterceptor();

        // 設(shè)置租戶處理器
        tenantInterceptor.setTenantLineHandler(new TenantLineHandler() {

            // 獲取當(dāng)前租戶ID
            @Override
            public Expression getTenantId() {
                String tenantId = TenantContext.getTenantId();
                if (tenantId == null) {
                    throw new RuntimeException("租戶ID不能為空");
                }
                return new StringValue(tenantId);
            }

            // 租戶ID對應(yīng)的字段名
            @Override
            public String getTenantIdColumn() {
                return "tenant_id";
            }

            // 默認(rèn)忽略租戶隔離的表(如系統(tǒng)配置表)
            @Override
            public boolean ignoreTable(String tableName) {
                return "system_config".equals(tableName) ||
                        "tenant_info".equals(tableName);
            }
        });

        interceptor.addInnerInterceptor(tenantInterceptor);
        return interceptor;
    }
}

攔截器注冊

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private TenantInterceptor tenantInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(tenantInterceptor)
                .addPathPatterns("/api/**")           // 攔截所有API接口
                .excludePathPatterns(
                        "/api/public/**",                 // 排除公共接口
                        "/api/auth/**",                   // 排除認(rèn)證接口
                        "/error"                         // 排除錯誤頁面
                );
    }
}

實體類與Mapper:

/**
 * 訂單實體(注意:不需要顯式定義tenant_id字段)
 */
@Data
@TableName("orders")
public class Order {
    private Long id;
    private String orderNo;
    private BigDecimal amount;
    private LocalDateTime createdTime;
    // 不需要定義tenant_id,MyBatisPlus會自動處理
}
/**
 * 訂單Mapper
 */
@Mapper
public interface OrderMapper extends BaseMapper<Order> {

}

業(yè)務(wù)服務(wù)層

@Service
@Slf4j
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;
    

    /**
     * 創(chuàng)建訂單 - 會自動注入tenant_id
     */
    @Transactional
    public void createOrder(String orderNo, BigDecimal amount) {
        Order order = new Order();
        order.setOrderNo(orderNo);
        order.setAmount(amount);
        order.setCreatedTime(LocalDateTime.now());

        int result = orderMapper.insert(order);
        log.info("創(chuàng)建訂單成功,ID: {}", order.getId());

    }

    /**
     * 查詢訂單列表
     */
    public List<Order> getOrders() {

        log.info("租戶[{}]查詢訂單", TenantContext.getTenantId());
        List<Order> orders = orderMapper.selectList(null);

        return orders;
    }
}

接口層

@RestController
@RequestMapping("/api/orders")
@Slf4j
public class OrderController {

    @Autowired
    private OrderService orderService;

    /**
     * 創(chuàng)建訂單接口
     */
    @PostMapping
    public ResponseEntity<String> createOrder(@RequestBody CreateOrderRequest request) {
        try {
            orderService.createOrder(request.getOrderNo(), request.getAmount());
            return ResponseEntity.ok("訂單創(chuàng)建成功");
        } catch (Exception e) {
            log.error("創(chuàng)建訂單失敗", e);
            return ResponseEntity.status(500).body("訂單創(chuàng)建失敗");
        }
    }

    /**
     * 查詢訂單列表
     */
    @GetMapping("/list")
    public List<Order> getOrders() {
        return orderService.getOrders();
    }



    @Data
    public static class CreateOrderRequest {
        private String orderNo;
        private BigDecimal amount;
    }
}

測試驗證

模擬租戶A的請求創(chuàng)建2個訂單

curl --location --request POST 'http://localhost:8080/api/orders' \
--header 'X-Tenant-ID: 001' \
--header 'Content-Type: application/json' \
--data-raw '{
    "orderNo":"A_001",
    "amount":"100"
}'
curl --location --request POST 'http://localhost:8080/api/orders' \
--header 'X-Tenant-ID: 001' \
--header 'Content-Type: application/json' \
--data-raw '{
    "orderNo":"A_001",
    "amount":"120"
}'

模擬租戶B的請求創(chuàng)建1個訂單

curl --location --request POST 'http://localhost:8080/api/orders' \
--header 'X-Tenant-ID: 002' \
--header 'Content-Type: application/json' \
--data-raw '{
    "orderNo":"B_001",
    "amount":"150"
}'

查詢租戶A訂單及租戶B訂單

curl --location --request GET 'http://localhost:8080/api/orders/list' \
--header 'X-Tenant-ID: 001'
curl --location --request GET 'http://localhost:8080/api/orders/list' \
--header 'X-Tenant-ID: 002'

可以看到租戶A返回了2個訂單,租戶B只返回了1個訂單

到此這篇關(guān)于MyBatisPlus實現(xiàn)多租戶數(shù)據(jù)隔離的文章就介紹到這了,更多相關(guān)MyBatisPlus 多租戶數(shù)據(jù)隔離內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Mybatis SQL運行流程源碼詳解

    Mybatis SQL運行流程源碼詳解

    這篇文章主要介紹了Mybatis SQL運行流程源碼詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-10-10
  • SpringCloud?Stream?整合RabbitMQ的基本步驟

    SpringCloud?Stream?整合RabbitMQ的基本步驟

    這篇文章主要介紹了SpringCloud?Stream?整合RabbitMQ的基本步驟,從項目介紹到生產(chǎn)者結(jié)合示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03
  • Java基礎(chǔ)知識之Java語言概述

    Java基礎(chǔ)知識之Java語言概述

    這篇文章主要介紹了Java基礎(chǔ)知識之Java語言概述,本文介紹了Java語言相關(guān)的基礎(chǔ)知識、歷史介紹、主要應(yīng)用方向等內(nèi)容,需要的朋友可以參考下
    2015-03-03
  • Java快速實現(xiàn)Word轉(zhuǎn)圖片功能的多種方法與實踐

    Java快速實現(xiàn)Word轉(zhuǎn)圖片功能的多種方法與實踐

    在軟件開發(fā)中,轉(zhuǎn)換文件格式是一個常見需求,尤其是將Word文件轉(zhuǎn)換為圖片格式,這在報表生成、文檔預(yù)覽、自動化處理等場景中非常有用,在 Java 中實現(xiàn)Word轉(zhuǎn)圖片的功能有多種思路,以下將介紹幾種常見的實現(xiàn)方式,并探討它們的優(yōu)缺點,需要的朋友可以參考下
    2025-08-08
  • Java之Arrays的各種功能和用法總結(jié)

    Java之Arrays的各種功能和用法總結(jié)

    數(shù)組在?Java?中是一種常用的數(shù)據(jù)結(jié)構(gòu),用于存儲和操作大量數(shù)據(jù)。Arrays?是我們在處理數(shù)組時的一把利器。它提供了豐富的方法和功能,使得數(shù)組操作變得更加簡單、高效和可靠。接下來我們一起看看?Arrays?的各種功能和用法,,需要的朋友可以參考下
    2023-05-05
  • Java內(nèi)存溢出實現(xiàn)原因及解決方案

    Java內(nèi)存溢出實現(xiàn)原因及解決方案

    這篇文章主要介紹了Java內(nèi)存溢出實現(xiàn)原因及解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03
  • Java代碼讀取文件緩存問題解決

    Java代碼讀取文件緩存問題解決

    最近遇到了一個Java文件讀取的緩存問題,打遠(yuǎn)程斷點出現(xiàn)的也是原來的老代碼參數(shù),本文就介紹一下解決方法,感興趣的可以了解一下
    2021-05-05
  • Java利用位運算實現(xiàn)加減運算詳解

    Java利用位運算實現(xiàn)加減運算詳解

    這篇文章主要為大家介紹了如何使用位運算來實現(xiàn)加減功能,也就是在整個運算過程中不能出現(xiàn)加減符號。文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2022-12-12
  • JVM垃圾回收分配及算法使用說明

    JVM垃圾回收分配及算法使用說明

    這篇文章主要介紹了Java垃圾回收機(jī)制,包括判斷對象是否可以回收、堆空間分配(年輕代、老年代及對應(yīng)回收算法)、永久代(元空間)的管理和其他垃圾回收算法(復(fù)制算法、標(biāo)記-整理算法)
    2025-12-12
  • Spring?@Bean?修飾方法時注入?yún)?shù)的操作方法

    Spring?@Bean?修飾方法時注入?yún)?shù)的操作方法

    對于 Spring 而言,IOC 容器中的 Bean 對象的創(chuàng)建和使用是一大重點,Spring 也為我們提供了注解方式創(chuàng)建 bean 對象:使用 @Bean,這篇文章主要介紹了Spring?@Bean?修飾方法時如何注入?yún)?shù),需要的朋友可以參考下
    2023-10-10

最新評論

天津市| 伊宁市| 井冈山市| 开鲁县| 合作市| 银川市| 湖南省| 水富县| 荆州市| 尼玛县| 治多县| 油尖旺区| 璧山县| 仙游县| 涡阳县| 泸西县| 东乡县| 南澳县| 杭锦旗| 澄迈县| 军事| 木里| 满城县| 白朗县| 玉山县| 措美县| 双辽市| 临沂市| 湟中县| 永嘉县| 五莲县| 贡觉县| 米林县| 广水市| 华池县| 屯留县| 荆门市| 宜丰县| 福鼎市| 昌邑市| 扎赉特旗|