Spring Boot集成MyBatis-Plus 自定義攔截器實現(xiàn)動態(tài)表名切換功能
Spring Boot集成MyBatis-Plus:自定義攔截器實現(xiàn)動態(tài)表名切換
一、引言
介紹動態(tài)表名的場景需求,比如多租戶系統(tǒng)、分表分庫,或者不同業(yè)務(wù)模塊共用一套代碼但操作不同表。說明 MyBatis-Plus 默認(rèn)綁定固定表名的問題。
二、項目配置
1. 集成 MyBatis-Plus
簡單說明如何在 Spring Boot 中引入 MyBatis-Plus 并配置。
2. 依賴添加
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>最新版本號</version> </dependency>
三、自定義攔截器實現(xiàn)動態(tài)表名
1. 攔截器原理
解釋 MyBatis 攔截器的核心概念,介紹 Interceptor
接口和 @Signature
注解。
2. 攔截器實現(xiàn)代碼
詳細(xì)展示攔截器的完整實現(xiàn):
@Component @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})}) public class DynamicTableInterceptor implements Interceptor { /** * 使用ThreadLocal來存儲線程特有的數(shù)據(jù),這里用于存儲動態(tài)表名 */ private static final ThreadLocal<String> TABLE_NAME_HOLDER = new ThreadLocal<>(); /** * 設(shè)置動態(tài)表名 * * @param tableName 需要設(shè)置的表名,由調(diào)用者指定 * 此方法允許在運行時動態(tài)地設(shè)置數(shù)據(jù)庫表名,以便在多數(shù)據(jù)源或動態(tài)表名的場景下靈活地切換表 */ public static void setDynamicTableName(String tableName) { TABLE_NAME_HOLDER.set(tableName); } /** * 清除當(dāng)前線程中的動態(tài)表名 * 此方法用于清除ThreadLocal中存儲的表名信息,以避免內(nèi)存泄漏 * 它應(yīng)在每次使用動態(tài)表名后調(diào)用,確保不會對后續(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) { /** * 從實體類的@tableName中獲取表名,必須在實體類上面添加@tableName注解 * 實體類中的@tableName注解中的值,必須是數(shù)據(jù)庫中的表名,否則會報錯 * 例如 :@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. 使用攔截器動態(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)行自定義操作。除了動態(tài)修改表名之外,攔截器還可以應(yīng)用于以下多種場景:
1. SQL 日志記錄與審計
- 場景:記錄每次執(zhí)行的 SQL 語句、參數(shù)、執(zhí)行時間等信息。
- 用途:用于 SQL 審計、性能監(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)前租戶信息自動添加過濾條件,確保數(shù)據(jù)隔離。
- 用途:實現(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ù)(如身份證號、電話等)在 SQL 操作時進(jìn)行自動加密或解密。
- 用途:提高數(shù)據(jù)安全性,確保數(shù)據(jù)存儲符合安全規(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. 自動分頁處理
- 場景:在執(zhí)行查詢時自動添加分頁邏輯,避免手動分頁處理。
- 用途:簡化分頁查詢的代碼。
示例:
@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)限,自動添加數(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 + "'"; // 動態(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. 動態(tài)數(shù)據(jù)源切換
場景:根據(jù)不同的業(yè)務(wù)需求動態(tài)選擇數(shù)據(jù)源。
用途:實現(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í)行的多個階段中注入自定義邏輯,從而實現(xiàn)多種高級功能。合理使用攔截器不僅能增強(qiáng)系統(tǒng)功能,還能提升性能和安全性。
到此這篇關(guān)于Spring Boot集成MyBatis-Plus 自定義攔截器實現(xiàn)動態(tài)表名切換功能的文章就介紹到這了,更多相關(guān)Spring Boot MyBatis-Plus 動態(tài)表名切換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
通過java字節(jié)碼分析學(xué)習(xí)對象初始化順序
今天用了jmock對進(jìn)行單元測試編碼,發(fā)現(xiàn)一個比較奇怪的語法,static使用方法,見下面例子2013-11-11使用Runnable實現(xiàn)數(shù)據(jù)共享
這篇文章主要為大家詳細(xì)介紹了如何使用Runnable實現(xiàn)數(shù)據(jù)共享,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-07-07IntelliJ?IDEA?2022.2.1最新永久激活破解教程(持續(xù)更新)
這篇文章主要介紹了IntelliJ?IDEA?2022.2.1最新永久激活破解教程(持續(xù)更新),小編測試這種激活工具也適用idea2022以下所有版本,本篇教程整理的比較詳細(xì),匯總了idea各個版本的激活工具,激活方法多種多樣,大家選擇一種即可,感興趣的朋友跟隨小編一起看看吧2022-09-09idea將maven項目改成Spring boot項目的方法步驟
這篇文章主要介紹了idea將maven項目改成Spring boot項目的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09