Spring Boot集成MyBatis-Plus 自定義攔截器實(shí)現(xiàn)動(dòng)態(tài)表名切換功能
Spring Boot集成MyBatis-Plus:自定義攔截器實(shí)現(xiàn)動(dòng)態(tài)表名切換

一、引言
介紹動(dòng)態(tài)表名的場景需求,比如多租戶系統(tǒng)、分表分庫,或者不同業(yè)務(wù)模塊共用一套代碼但操作不同表。說明 MyBatis-Plus 默認(rèn)綁定固定表名的問題。
二、項(xiàng)目配置
1. 集成 MyBatis-Plus
簡單說明如何在 Spring Boot 中引入 MyBatis-Plus 并配置。
2. 依賴添加
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>最新版本號(hào)</version>
</dependency>三、自定義攔截器實(shí)現(xiàn)動(dòng)態(tài)表名
1. 攔截器原理
解釋 MyBatis 攔截器的核心概念,介紹 Interceptor 接口和 @Signature 注解。
2. 攔截器實(shí)現(xiàn)代碼
詳細(xì)展示攔截器的完整實(shí)現(xiàn):
@Component
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DynamicTableInterceptor implements Interceptor {
/**
* 使用ThreadLocal來存儲(chǔ)線程特有的數(shù)據(jù),這里用于存儲(chǔ)動(dòng)態(tài)表名
*/
private static final ThreadLocal<String> TABLE_NAME_HOLDER = new ThreadLocal<>();
/**
* 設(shè)置動(dòng)態(tài)表名
*
* @param tableName 需要設(shè)置的表名,由調(diào)用者指定
* 此方法允許在運(yùn)行時(shí)動(dòng)態(tài)地設(shè)置數(shù)據(jù)庫表名,以便在多數(shù)據(jù)源或動(dòng)態(tài)表名的場景下靈活地切換表
*/
public static void setDynamicTableName(String tableName) {
TABLE_NAME_HOLDER.set(tableName);
}
/**
* 清除當(dāng)前線程中的動(dòng)態(tài)表名
* 此方法用于清除ThreadLocal中存儲(chǔ)的表名信息,以避免內(nèi)存泄漏
* 它應(yīng)在每次使用動(dòng)態(tài)表名后調(diào)用,確保不會(huì)對后續(xù)的請求產(chǎn)生影響
*/
public static void clearDynamicTableName() {
TABLE_NAME_HOLDER.remove();
}
/**
* 攔截器
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler handler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = handler.getBoundSql();
String tableName = TABLE_NAME_HOLDER.get();
if (tableName != null) {
/**
* 從實(shí)體類的@tableName中獲取表名,必須在實(shí)體類上面添加@tableName注解
* 實(shí)體類中的@tableName注解中的值,必須是數(shù)據(jù)庫中的表名,否則會(huì)報(bào)錯(cuò)
* 例如 :@TableName("t_user_"),數(shù)據(jù)庫是根據(jù)月份來的,在這里替換則需要根據(jù)當(dāng)前月份進(jìn)行拼接表名
*/
Class<?> entityType = boundSql.getParameterObject().getClass();
String oldTableName = entityType.getAnnotation(TableName.class).value();
String newSql = boundSql.getSql().replace(oldTableName, tableName);
Field sqlField = boundSql.getClass().getDeclaredField("sql");
sqlField.setAccessible(true);
sqlField.set(boundSql, newSql);
}
return invocation.proceed();
}
}3. 使用攔截器動(dòng)態(tài)設(shè)置表名
DynamicTableInterceptor.setDynamicTableName("your_dynamic_table_name");
try {
myService.saveOrUpdateBatch(entities); // MyBatis-Plus 操作
} finally {
DynamicTableInterceptor.clearDynamicTableName();
}四、其他場景
MyBatis 攔截器(Interceptor)是 MyBatis 提供的強(qiáng)大擴(kuò)展機(jī)制,可以攔截執(zhí)行過程中的不同階段并進(jìn)行自定義操作。除了動(dòng)態(tài)修改表名之外,攔截器還可以應(yīng)用于以下多種場景:
1. SQL 日志記錄與審計(jì)
- 場景:記錄每次執(zhí)行的 SQL 語句、參數(shù)、執(zhí)行時(shí)間等信息。
- 用途:用于 SQL 審計(jì)、性能監(jiān)控、問題排查。
示例:
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler handler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = handler.getBoundSql();
long startTime = System.currentTimeMillis();
Object result = invocation.proceed();
long endTime = System.currentTimeMillis();
System.out.println("SQL: " + boundSql.getSql() + " Execution Time: " + (endTime - startTime) + "ms");
return result;
}2. 多租戶數(shù)據(jù)隔離
- 場景:根據(jù)當(dāng)前租戶信息自動(dòng)添加過濾條件,確保數(shù)據(jù)隔離。
- 用途:實(shí)現(xiàn) SaaS 系統(tǒng)中不同租戶的數(shù)據(jù)訪問控制。
示例:
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler handler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = handler.getBoundSql();
String originalSql = boundSql.getSql();
String tenantId = TenantContext.getCurrentTenantId();
String modifiedSql = originalSql + " WHERE tenant_id = " + tenantId;
Field sqlField = boundSql.getClass().getDeclaredField("sql");
sqlField.setAccessible(true);
sqlField.set(boundSql, modifiedSql);
return invocation.proceed();
}3. SQL 參數(shù)加密與解密
- 場景:對敏感數(shù)據(jù)(如身份證號(hào)、電話等)在 SQL 操作時(shí)進(jìn)行自動(dòng)加密或解密。
- 用途:提高數(shù)據(jù)安全性,確保數(shù)據(jù)存儲(chǔ)符合安全規(guī)范。
示例:
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler handler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
// 自定義加密邏輯
// 加密處理參數(shù)
return invocation.proceed();
}4. 自動(dòng)分頁處理
- 場景:在執(zhí)行查詢時(shí)自動(dòng)添加分頁邏輯,避免手動(dòng)分頁處理。
- 用途:簡化分頁查詢的代碼。
示例:
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler handler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = handler.getBoundSql();
String originalSql = boundSql.getSql();
String paginatedSql = originalSql + " LIMIT " + offset + ", " + limit;
Field sqlField = boundSql.getClass().getDeclaredField("sql");
sqlField.setAccessible(true);
sqlField.set(boundSql, paginatedSql);
return invocation.proceed();
}5. 數(shù)據(jù)權(quán)限控制
- 場景:根據(jù)用戶角色或權(quán)限,自動(dòng)添加數(shù)據(jù)過濾條件。
- 用途:確保用戶只能訪問被授權(quán)的數(shù)據(jù)。
示例:
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler handler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = handler.getBoundSql();
String originalSql = boundSql.getSql();
String userRole = SecurityContext.getCurrentUserRole();
String restrictedSql = originalSql + " AND role = '" + userRole + "'";
// 動(dòng)態(tài)修改 SQL
return invocation.proceed();
}6. 緩存增強(qiáng)
- 場景:自定義緩存邏輯,攔截查詢請求,先檢查緩存是否命中。
- 用途:減少數(shù)據(jù)庫訪問次數(shù),提高性能。
示例:
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 檢查緩存
Object cachedResult = CacheManager.get(boundSql.getSql());
if (cachedResult != null) {
return cachedResult;
}
// 如果沒有命中,執(zhí)行查詢
Object result = invocation.proceed();
// 存入緩存
return result;
}7. 動(dòng)態(tài)數(shù)據(jù)源切換
場景:根據(jù)不同的業(yè)務(wù)需求動(dòng)態(tài)選擇數(shù)據(jù)源。
用途:實(shí)現(xiàn)讀寫分離或多數(shù)據(jù)庫支持。
示例:
@Override
public Object intercept(Invocation invocation) throws Throwable {
String dataSourceKey = DataSourceContextHolder.getDataSourceKey();
DynamicDataSource.setDataSourceKey(dataSourceKey);
return invocation.proceed();
}總結(jié)
MyBatis 攔截器為開發(fā)者提供了靈活的擴(kuò)展能力,可以在 SQL 執(zhí)行的多個(gè)階段中注入自定義邏輯,從而實(shí)現(xiàn)多種高級功能。合理使用攔截器不僅能增強(qiáng)系統(tǒng)功能,還能提升性能和安全性。
到此這篇關(guān)于Spring Boot集成MyBatis-Plus 自定義攔截器實(shí)現(xiàn)動(dòng)態(tài)表名切換功能的文章就介紹到這了,更多相關(guān)Spring Boot MyBatis-Plus 動(dòng)態(tài)表名切換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
通過java字節(jié)碼分析學(xué)習(xí)對象初始化順序
今天用了jmock對進(jìn)行單元測試編碼,發(fā)現(xiàn)一個(gè)比較奇怪的語法,static使用方法,見下面例子2013-11-11
使用Runnable實(shí)現(xiàn)數(shù)據(jù)共享
這篇文章主要為大家詳細(xì)介紹了如何使用Runnable實(shí)現(xiàn)數(shù)據(jù)共享,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07
IntelliJ?IDEA?2022.2.1最新永久激活破解教程(持續(xù)更新)
這篇文章主要介紹了IntelliJ?IDEA?2022.2.1最新永久激活破解教程(持續(xù)更新),小編測試這種激活工具也適用idea2022以下所有版本,本篇教程整理的比較詳細(xì),匯總了idea各個(gè)版本的激活工具,激活方法多種多樣,大家選擇一種即可,感興趣的朋友跟隨小編一起看看吧2022-09-09
idea將maven項(xiàng)目改成Spring boot項(xiàng)目的方法步驟
這篇文章主要介紹了idea將maven項(xiàng)目改成Spring boot項(xiàng)目的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

