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)表名的場(chǎng)景需求,比如多租戶系統(tǒng)、分表分庫(kù),或者不同業(yè)務(wù)模塊共用一套代碼但操作不同表。說(shuō)明 MyBatis-Plus 默認(rèn)綁定固定表名的問(wèn)題。
二、項(xiàng)目配置
1. 集成 MyBatis-Plus
簡(jiǎn)單說(shuō)明如何在 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來(lái)存儲(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ù)庫(kù)表名,以便在多數(shù)據(jù)源或動(dòng)態(tài)表名的場(chǎng)景下靈活地切換表 */ 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ì)對(duì)后續(xù)的請(qǐng)求產(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ù)庫(kù)中的表名,否則會(huì)報(bào)錯(cuò) * 例如 :@TableName("t_user_"),數(shù)據(jù)庫(kù)是根據(jù)月份來(lái)的,在這里替換則需要根據(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(); }
四、其他場(chǎng)景
MyBatis 攔截器(Interceptor)是 MyBatis 提供的強(qiáng)大擴(kuò)展機(jī)制,可以攔截執(zhí)行過(guò)程中的不同階段并進(jìn)行自定義操作。除了動(dòng)態(tài)修改表名之外,攔截器還可以應(yīng)用于以下多種場(chǎng)景:
1. SQL 日志記錄與審計(jì)
- 場(chǎng)景:記錄每次執(zhí)行的 SQL 語(yǔ)句、參數(shù)、執(zhí)行時(shí)間等信息。
- 用途:用于 SQL 審計(jì)、性能監(jiān)控、問(wè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ù)隔離
- 場(chǎng)景:根據(jù)當(dāng)前租戶信息自動(dòng)添加過(guò)濾條件,確保數(shù)據(jù)隔離。
- 用途:實(shí)現(xiàn) SaaS 系統(tǒng)中不同租戶的數(shù)據(jù)訪問(wèn)控制。
示例:
@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ù)加密與解密
- 場(chǎng)景:對(duì)敏感數(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)分頁(yè)處理
- 場(chǎng)景:在執(zhí)行查詢時(shí)自動(dòng)添加分頁(yè)邏輯,避免手動(dòng)分頁(yè)處理。
- 用途:簡(jiǎn)化分頁(yè)查詢的代碼。
示例:
@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)限控制
- 場(chǎng)景:根據(jù)用戶角色或權(quán)限,自動(dòng)添加數(shù)據(jù)過(guò)濾條件。
- 用途:確保用戶只能訪問(wèn)被授權(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)
- 場(chǎng)景:自定義緩存邏輯,攔截查詢請(qǐng)求,先檢查緩存是否命中。
- 用途:減少數(shù)據(jù)庫(kù)訪問(wèn)次數(shù),提高性能。
示例:
@Override public Object intercept(Invocation invocation) throws Throwable { // 檢查緩存 Object cachedResult = CacheManager.get(boundSql.getSql()); if (cachedResult != null) { return cachedResult; } // 如果沒(méi)有命中,執(zhí)行查詢 Object result = invocation.proceed(); // 存入緩存 return result; }
7. 動(dòng)態(tài)數(shù)據(jù)源切換
場(chǎng)景:根據(jù)不同的業(yè)務(wù)需求動(dòng)態(tài)選擇數(shù)據(jù)源。
用途:實(shí)現(xiàn)讀寫分離或多數(shù)據(jù)庫(kù)支持。
示例:
@Override public Object intercept(Invocation invocation) throws Throwable { String dataSourceKey = DataSourceContextHolder.getDataSourceKey(); DynamicDataSource.setDataSourceKey(dataSourceKey); return invocation.proceed(); }
總結(jié)
MyBatis 攔截器為開(kāi)發(fā)者提供了靈活的擴(kuò)展能力,可以在 SQL 執(zhí)行的多個(gè)階段中注入自定義邏輯,從而實(shí)現(xiàn)多種高級(jí)功能。合理使用攔截器不僅能增強(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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
通過(guò)java字節(jié)碼分析學(xué)習(xí)對(duì)象初始化順序
今天用了jmock對(duì)進(jìn)行單元測(cè)試編碼,發(fā)現(xiàn)一個(gè)比較奇怪的語(yǔ)法,static使用方法,見(jiàn)下面例子2013-11-11使用Runnable實(shí)現(xiàn)數(shù)據(jù)共享
這篇文章主要為大家詳細(xì)介紹了如何使用Runnable實(shí)現(xiàn)數(shù)據(jù)共享,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07IntelliJ?IDEA?2022.2.1最新永久激活破解教程(持續(xù)更新)
這篇文章主要介紹了IntelliJ?IDEA?2022.2.1最新永久激活破解教程(持續(xù)更新),小編測(cè)試這種激活工具也適用idea2022以下所有版本,本篇教程整理的比較詳細(xì),匯總了idea各個(gè)版本的激活工具,激活方法多種多樣,大家選擇一種即可,感興趣的朋友跟隨小編一起看看吧2022-09-09idea將maven項(xiàng)目改成Spring boot項(xiàng)目的方法步驟
這篇文章主要介紹了idea將maven項(xiàng)目改成Spring boot項(xiàng)目的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Jdbc的步驟以及簡(jiǎn)單實(shí)現(xiàn)代碼
下面小編就為大家?guī)?lái)一篇Jdbc的步驟以及簡(jiǎn)單實(shí)現(xiàn)代碼。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-07-07