深入 MyBatis-Plus 外掛:解鎖高階資料庫功能

ccm03發表於2024-11-09

一、關於Mybatis-Plus外掛

1.1 簡介

Mybatis-Plus 提供了豐富的外掛機制,這些外掛可以幫助開發者更方便地擴充套件 Mybatis 的功能,提升開發效率、最佳化效能和實現一些常用的功能。

953921f3-2296-4535-9f98-4d4c3d801e2d

1.2 實現原理

Mybatis-Plus 的外掛實現是基於 MyBatis 的攔截器機制, 這些外掛透過 MybatisPlusInterceptor​ 來實現對 MyBatis 執行過程的攔截和增強。

MyBatis 外掛本質上是對 SQL 執行過程的攔截和擴充套件,Mybatis-Plus 外掛透過在 MyBatis 的執行生命週期中插入攔截器來實現一些增強功能。透過這種方式,Mybatis-Plus 可以實現分頁、效能分析、樂觀鎖等功能的自動化處理。

MybatisPlusInterceptor 概覽

MybatisPlusInterceptor​ 是 MyBatis-Plus 的核心外掛,它代理了 MyBatis 的 Executor#query​、Executor#update​ 和 StatementHandler#prepare​ 方法,允許在這些方法執行前後插入自定義邏輯。

image

屬性

MybatisPlusInterceptor​ 有一個關鍵屬性 interceptors​,它是一個 List<InnerInterceptor>​ 型別的集合,用於儲存所有要應用的內部攔截器。

InnerInterceptor 介面

所有 MyBatis-Plus 提供的外掛都實現了 InnerInterceptor​ 介面,這個介面定義了外掛的基本行為。目前,MyBatis-Plus 提供了以下外掛:

  • 自動分頁: PaginationInnerInterceptor
  • 多租戶: TenantLineInnerInterceptor
  • 動態表名: DynamicTableNameInnerInterceptor
  • 樂觀鎖: OptimisticLockerInnerInterceptor
  • SQL 效能規範: IllegalSQLInnerInterceptor
  • 防止全表更新與刪除: BlockAttackInnerInterceptor

1.3 配置方式

外掛的配置可以在 Spring 配置中進行,也可以在 Spring Boot 專案中透過 Java 配置來新增。以下是兩種配置方式的示例:

  • Spring 配置:在 Spring 配置中,需要建立 MybatisPlusInterceptor​ 的例項,並將它新增到 MyBatis 的外掛列表中。
  • Spring Boot 配置:在 Spring Boot 專案中,可以透過 Java 配置來新增外掛,例如新增分頁外掛。

Spring Boot 配置示例

@Configuration
@MapperScan("scan.your.mapper.package")
public class MybatisPlusConfig {

    /**
     * 新增分頁外掛
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

配置多個外掛

@Configuration
public class MyBatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        // 新增分頁外掛
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));

        // 新增效能分析外掛
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        performanceInterceptor.setMaxTime(1000); // 設定SQL最大執行時間,單位為毫秒
        interceptor.addInnerInterceptor(performanceInterceptor);

        // 新增防全表更新與刪除外掛
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());

        return interceptor;
    }
}

注意

使用多個外掛時,需要注意它們的順序。建議的順序是:

  1. 多租戶、動態表名
  2. 分頁、樂觀鎖
  3. SQL 效能規範、防止全表更新與刪除

總結:對 SQL 進行單次改造的外掛應優先放入,不對 SQL 進行改造的外掛最後放入。

二、分頁外掛(PaginationInnerInterceptor​)

2.1 關於

簡介

MyBatis-Plus 的分頁外掛 PaginationInnerInterceptor​ 提供了強大的分頁功能,支援多種資料庫,使得分頁查詢變得簡單高效。用時只需要在查詢方法中傳入Page<T>​物件,外掛會自動處理分頁相關的SQL構建和結果集解析。

image

主要功能

  1. 自動分頁

    • 透過在查詢時自動新增 LIMIT​ 和 OFFSET​ 等 SQL 關鍵字,來實現分頁功能。
  2. 相容性

    • 支援多種資料庫的分頁語法,確保在不同資料庫上都能正常工作。
  3. 動態引數

    • 可以動態地根據使用者的請求引數(如頁碼和每頁大小)生成分頁資訊,而無需手動處理 SQL。
  4. 效能最佳化

    • 在執行分頁查詢時,透過設定合理的引數,能夠減少查詢的時間複雜度,提高查詢效率。

關鍵引數

  • DbType:指定資料庫型別,影響生成的分頁 SQL 語句。例如,DbType.MYSQL​ 會生成適用於 MySQL 的分頁語句。
  • setOverflow:允許配置是否允許請求的頁碼超出最大頁碼範圍(例如,返回最後一頁的資料)。
  • setMaxLimit:可以設定每頁最大記錄數,避免使用者請求過大的分頁資料。

2.2 使用

配置外掛

@Configuration
@MapperScan("scan.your.mapper.package")
public class MybatisPlusConfig {

    /**
     * 新增分頁外掛
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多個外掛, 切記分頁最後新增
        // 如果有多資料來源可以不配具體型別, 否則都建議配上具體的 DbType
        return interceptor;
    }
}

分頁查詢

Page<User> page = new Page<>(1, 10);  // 當前頁, 每頁記錄數
IPage<User> userPage = userMapper.selectPage(page, null);  

三、效能分析外掛(PerformanceInterceptor​)

3.1 關於

簡介

效能分析外掛(PerformanceInterceptor​)是 MyBatis-Plus 提供的一個非常有用的工具,它可以用來監控 SQL 語句的執行時間,幫助開發者及時發現和最佳化慢查詢問題。

3.2 使用

配置外掛

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PerformanceInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 新增效能分析外掛
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        performanceInterceptor.setMaxTime(1000); // 設定SQL最大執行時間,單位為毫秒
        interceptor.addInnerInterceptor(performanceInterceptor);
        return interceptor;
    }
}

配置日誌輸出

為了更好地監控 SQL 語句的執行情況,可以配置日誌輸出。在 application.properties​ 或 application.yml​ 檔案中新增日誌配置:

logging:
  level:
    com.baomidou.mybatisplus: DEBUG

  • SQL 執行時間記錄:每次執行 SQL 語句時,外掛會記錄執行時間。
  • 超時處理:如果 SQL 語句的執行時間超過 setMaxTime​ 方法設定的閾值(預設為 0,表示不限制),外掛會記錄一條警告日誌或丟擲異常,具體行為取決於配置。

如果 SQL 語句執行時間超過設定的閾值,日誌輸出可能如下所示:

2024-11-08 10:41:00 [http-nio-8080-exec-1] WARN  c.b.mybatisplus.extension.plugins.inner.PerformanceInterceptor - [performance] SQL Execution Time: 1500 ms

透過以上步驟,你可以在 MyBatis-Plus 中輕鬆配置和使用效能分析外掛,幫助你及時發現和最佳化慢查詢問題。

四、防全表更新與刪除外掛(BlockAttackInterceptor​)

4.1 關於

簡介

MyBatis-Plus 提供了一個防全表更新與刪除外掛(BlockAttackInterceptor​),該外掛可以防止在沒有 WHERE 條件的情況下執行全表更新或刪除操作,從而避免誤操作導致的資料丟失或損壞

使用

配置外掛

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 新增防全表更新與刪除外掛
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        return interceptor;
    }
}

測試

在控制器層中呼叫 Service 層的方法進行查詢。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/delete-all")
    public String deleteAllUsers() {
        try {
            userService.remove(null); // 嘗試刪除所有使用者
            return "All users deleted successfully";
        } catch (Exception e) {
            return "Failed to delete all users: " + e.getMessage();
        }
    }

    @PostMapping("/update-all")
    public String updateAllUsers() {
        try {
            User user = new User();
            user.setName("Updated Name");
            userService.updateById(user); // 嘗試更新所有使用者
            return "All users updated successfully";
        } catch (Exception e) {
            return "Failed to update all users: " + e.getMessage();
        }
    }
}
  1. 嘗試刪除所有使用者:訪問 /users/delete-all​ 介面。

    • 如果沒有 WHERE 條件,外掛會丟擲異常並阻止刪除操作。

    • 控制檯輸出示例:

      Failed to delete all users: Cannot execute delete operation without where condition!
      
  2. 嘗試更新所有使用者:訪問 /users/update-all​ 介面。

    • 如果沒有 WHERE 條件,外掛會丟擲異常並阻止更新操作。

    • 控制檯輸出示例:

      Failed to update all users: Cannot execute update operation without where condition!
      

五、自定義外掛

如果內建外掛不能滿足需求,可以自定義外掛。自定義外掛需要實現 Interceptor​ 或 InnerInterceptor​ 介面,並在 intercept​ 方法中實現自定義邏輯。

示例:

import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;

import java.sql.Connection;

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class CustomInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 自定義邏輯
        System.out.println("CustomInterceptor: Before SQL execution");
        Object result = invocation.proceed();
        System.out.println("CustomInterceptor: After SQL execution");
        return result;
    }

    @Override
    public Object plugin(Object target) {
        return Interceptor.super.plugin(target);
    }

    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }
}

註冊自定義外掛:

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new CustomInterceptor());
    return interceptor;
}

透過上述機制和介面,MyBatis-Plus 提供了靈活的外掛擴充套件能力,使開發者可以根據具體需求定製化功能。

相關文章