MyBatis Plus中InnerInterceptor的實(shí)現(xiàn)
在 Spring Boot 項(xiàng)目中使用 MyBatis Plus 時(shí),你可能會(huì)遇到 InnerInterceptor 這個(gè)概念。 InnerInterceptor 是 MyBatis Plus 提供的一種輕量級(jí) SQL 攔截器,它與傳統(tǒng)的 MyBatis 攔截器(Interceptor)有所不同,具有更簡單、更高效的特點(diǎn),并且更專注于 SQL 執(zhí)行層面的攔截。本文將詳細(xì)介紹 InnerInterceptor 的原理、用法和最佳實(shí)踐,并提供代碼示例。
一、為什么需要 InnerInterceptor?
- 更輕量級(jí): 相比于傳統(tǒng)的 Interceptor,InnerInterceptor 更加輕量級(jí),減少了不必要的攔截開銷,提高了性能。
- 更專注于 SQL 執(zhí)行: InnerInterceptor 專注于 SQL 執(zhí)行層面,可以讓你更方便地修改 SQL 語句、參數(shù)或結(jié)果。
- 簡化配置: InnerInterceptor 的配置更加簡單,無需手動(dòng)注冊(cè),MyBatis Plus 會(huì)自動(dòng)識(shí)別并注冊(cè)。
- 易于擴(kuò)展:你可以通過實(shí)現(xiàn) InnerInterceptor 接口,自定義 SQL 攔截邏輯。
- 與 MyBatis Plus 無縫集成:InnerInterceptor 與 MyBatis Plus 的其他功能無縫集成,可以更好地發(fā)揮 MyBatis Plus 的優(yōu)勢(shì)。
- 內(nèi)置豐富功能: MyBatis Plus 提供了許多內(nèi)置的 InnerInterceptor 實(shí)現(xiàn),如分頁插件、樂觀鎖插件、SQL性能分析插件等,可以直接使用。
二、InnerInterceptor 與 Interceptor 的區(qū)別
- 攔截范圍:
Interceptor
可以攔截 MyBatis 的 Executor、ParameterHandler、ResultSetHandler 和 StatementHandler 等組件,攔截范圍更廣。InnerInterceptor
主要攔截 SQL 執(zhí)行過程中的 StatementHandler,攔截范圍更窄,但更專注于 SQL 執(zhí)行。 - 執(zhí)行時(shí)機(jī):
Interceptor
可以攔截 SQL 執(zhí)行過程中的多個(gè)階段,例如參數(shù)處理、SQL 預(yù)編譯、結(jié)果處理等。InnerInterceptor
主要攔截 StatementHandler 的 prepare 和 query 方法,更專注于 SQL 語句的準(zhǔn)備和執(zhí)行階段。 - 配置方式:
Interceptor
需要在 MyBatis 配置文件或 Spring Bean 中手動(dòng)注冊(cè)。InnerInterceptor
通過 MyBatis Plus 提供的 MybatisPlusInterceptor 統(tǒng)一注冊(cè)管理,無需手動(dòng)注冊(cè)。 - 代碼復(fù)雜度:
Interceptor
的代碼相對(duì)復(fù)雜,需要處理 Invocation 對(duì)象,并手動(dòng)調(diào)用 proceed 方法。InnerInterceptor
的代碼更加簡潔,只需要重寫對(duì)應(yīng)的方法。 - 性能:
Interceptor
由于攔截范圍更廣,可能會(huì)帶來一定的性能開銷。InnerInterceptor
由于攔截范圍更窄,性能更高。
三、InnerInterceptor 的核心方法
- void
beforePrepare
(StatementHandler sh, Connection connection,Integer transactionTimeout): 在 SQL 語句預(yù)編譯之前調(diào)用。 - void
beforeQuery
(StatementHandler sh, Connection connection, Integer transactionTimeout): 在 SQL 語句執(zhí)行之前調(diào)用。 - void
afterQuery
(StatementHandler sh, Connection connection, Integer transactionTimeout, Object result): 在 SQL 查詢執(zhí)行后調(diào)用。 - void
beforeUpdate
(StatementHandler sh, Connection connection, Integer transactionTimeout): 在執(zhí)行 INSERT 或 UPDATE 語句之前調(diào)用。 - void
afterUpdate
(StatementHandler sh, Connection connection, Integer transactionTimeout,Object result): 在執(zhí)行 INSERT 或 UPDATE 語句之后調(diào)用。
四、實(shí)踐:使用 InnerInterceptor 修改 SQL 語句
4.1 創(chuàng)建 InnerInterceptor 實(shí)現(xiàn)類:
import com.baomidou.mybatisplus.core.toolkit.PluginUtils; import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; import lombok.extern.slf4j.Slf4j; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.parser.CCJSqlParserManager; import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.springframework.stereotype.Component; import java.io.StringReader; import java.sql.SQLException; @Component @Slf4j public class MyInnerInterceptor implements InnerInterceptor { @Override public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { String sql = boundSql.getSql(); try { PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql); //sql處理 String filterSql = addFilterCondition(sql); log.info("修改過后的sql:{}", filterSql); //修改sql mpBs.sql(filterSql); } catch (Exception e) { log.warn("動(dòng)態(tài)修改sql:{}異常", sql, e); throw new SQLException("添加數(shù)據(jù)權(quán)限異常"); } } public String addFilterCondition(String originalSql) throws JSQLParserException { CCJSqlParserManager parserManager = new CCJSqlParserManager(); Select select = (Select) parserManager.parse(new StringReader(originalSql)); PlainSelect plain = (PlainSelect) select.getSelectBody(); Expression where_expression = plain.getWhere(); // 這里可以根據(jù)需要增加過濾條件 if (where_expression == null) { plain.setWhere(CCJSqlParserUtil.parseCondExpression("age = 35")); } return plain.toString(); } }
4.2 配置 MybatisPlusInterceptor
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.extend.chk.interceptor.MyInnerInterceptor; import org.apache.ibatis.session.SqlSessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; import java.util.List; @Configuration public class MyBatisPlusConfig { @Autowired private List<SqlSessionFactory> sqlSessionFactoryList; @Autowired private MyInnerInterceptor myInnerInterceptor; /** * 添加Mybatis攔截器 * 主要是為了保證數(shù)據(jù)權(quán)限攔截器在分頁插件攔截器之前執(zhí)行sql的修改,如果不在這里手動(dòng)添加的話,PageInterceptor會(huì)先執(zhí)行 * 先添加的攔截器后執(zhí)行 */ @PostConstruct public void addMybatisInterceptor() { for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) { org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration(); //將數(shù)據(jù)權(quán)限攔截器添加到MybatisPlusInterceptor攔截器鏈 MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(myInnerInterceptor); //先添加PageHelper分頁插件攔截器,再添加MybatisPlusInterceptor攔截器 //configuration.addInterceptor(new PageInterceptor()); configuration.addInterceptor(mybatisPlusInterceptor); } } }
4.3 使用 InnerInterceptor
現(xiàn)在,你執(zhí)行任何 SQL 語句,都會(huì)被 InnerInterceptor 攔截,你可以看到 SQL 語句已經(jīng)被修改。
修改過后的sql:SELECT count(0) FROM t_user_info WHERE age = 35 修改過后的sql:SELECT id, name, password, age, status, last_login_time, token, create_by, create_time, update_by, update_time, remark FROM t_user_info WHERE age = 35 LIMIT ?
五、內(nèi)置攔截器
除了自定義攔截器外,MyBatis-Plus 還提供了多個(gè)內(nèi)置攔截器,可以直接使用或作為參考來創(chuàng)建自己的攔截器。以下是幾個(gè)常用的內(nèi)置攔截器:
PaginationInterceptor
:分頁插件,支持多種數(shù)據(jù)庫的分頁查詢。PerformanceAnalyzerInterceptor
:性能分析插件,記錄每條 SQL 的執(zhí)行時(shí)間和影響行數(shù)。OptimisticLockerInterceptor
:樂觀鎖插件,用于防止并發(fā)更新時(shí)的數(shù)據(jù)覆蓋問題。BlockAttackInterceptor
:阻止惡意攻擊插件,防止批量刪除或更新操作導(dǎo)致數(shù)據(jù)丟失。
六、常見應(yīng)用場(chǎng)景
- SQL 日志記錄:如上文所示,記錄每次 SQL 執(zhí)行的時(shí)間、參數(shù)及結(jié)果,便于調(diào)試和性能分析。
- 分頁插件:動(dòng)態(tài)地為查詢語句添加分頁條件,而無需修改原有的 Mapper 文件。
- SQL 性能監(jiān)控:統(tǒng)計(jì)每條 SQL 的執(zhí)行次數(shù)、平均耗時(shí)等指標(biāo),幫助識(shí)別潛在的性能瓶頸。
- 緩存實(shí)現(xiàn):基于攔截器實(shí)現(xiàn)簡單的查詢結(jié)果緩存,減少不必要的數(shù)據(jù)庫訪問。
- 數(shù)據(jù)脫敏:在查詢結(jié)果返回之前,對(duì)敏感字段進(jìn)行加密或替換,確保數(shù)據(jù)安全。
- 權(quán)限控制:在 SQL 執(zhí)行前檢查用戶權(quán)限,防止未經(jīng)授權(quán)的操作。
七、最佳實(shí)踐
- 按需選擇攔截器: 根據(jù)實(shí)際需求選擇合適的攔截器,如果需要修改 SQL 語句、參數(shù)或結(jié)果,可以使用 InnerInterceptor,如果需要攔截 MyBatis 的其他組件,可以使用 Interceptor。
- 細(xì)粒度控制: 可以根據(jù) MappedStatement 的 ID 或 SQL 語句內(nèi)容,細(xì)粒度控制 InnerInterceptor 的執(zhí)行范圍。
- 使用內(nèi)置的 InnerInterceptor: MyBatis Plus 提供了許多內(nèi)置的 InnerInterceptor 實(shí)現(xiàn),如分頁插件、樂觀鎖插件、SQL 性能分析插件等,可以直接使用,無需重復(fù)開發(fā)。
- 避免耗時(shí)操作: InnerInterceptor 會(huì)在 SQL 執(zhí)行的關(guān)鍵節(jié)點(diǎn)執(zhí)行,避免在其中執(zhí)行耗時(shí)的操作,以免影響性能。
- 異常處理: 在 InnerInterceptor 方法中使用 try-catch 代碼塊處理可能拋出的異常,避免影響正常業(yè)務(wù)邏輯。
- 配置順序: 如果存在多個(gè) InnerInterceptor,Mybatis Plus 會(huì)根據(jù) addInnerInterceptor 方法的調(diào)用順序進(jìn)行執(zhí)行。
- 使用 MyBatis Plus 工具類: MyBatis Plus 提供了一些工具類,例如 PluginUtils,可以方便地訪問和修改 SQL 語句、參數(shù)等信息。
到此這篇關(guān)于MyBatis Plus中InnerInterceptor的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)MyBatisPlus InnerInterceptor內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在已經(jīng)使用mybatis的項(xiàng)目里引入mybatis-plus,結(jié)果不能共存的解決
這篇文章主要介紹了在已經(jīng)使用mybatis的項(xiàng)目里引入mybatis-plus,結(jié)果不能共存的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03多數(shù)據(jù)源如何實(shí)現(xiàn)事務(wù)管理
Spring中涉及三個(gè)核心事務(wù)處理接口:PlatformTransactionManager、TransactionDefinition和TransactionStatus,PlatformTransactionManager提供事務(wù)操作的基本方法,如獲取事務(wù)、提交和回滾2024-09-09Java 實(shí)戰(zhàn)項(xiàng)目之在線點(diǎn)餐系統(tǒng)的實(shí)現(xiàn)流程
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+jsp+mysql+maven實(shí)現(xiàn)一個(gè)在線點(diǎn)餐系統(tǒng),大家可以在過程中查缺補(bǔ)漏,提升水平2021-11-11在IDEA中配置tomcat并創(chuàng)建tomcat項(xiàng)目的圖文教程
這篇文章主要介紹了在IDEA中配置tomcat并創(chuàng)建tomcat項(xiàng)目的圖文教程,需要的朋友可以參考下2020-07-07java按照模板導(dǎo)出pdf或word文件詳細(xì)代碼
有時(shí)候業(yè)務(wù)中我們需要使用pdf模板生成一份pdf文件,下面這篇文章主要給大家介紹了關(guān)于java按照模板導(dǎo)出pdf或word文件的相關(guān)資料,文中給出了詳細(xì)的代碼示例,需要的朋友可以參考下2023-11-11springboot如何根據(jù)不同的日志級(jí)別顯示不同的顏色
這篇文章主要介紹了springboot如何根據(jù)不同的日志級(jí)別顯示不同的顏色問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08SprinBoot如何集成參數(shù)校驗(yàn)Validator及參數(shù)校驗(yàn)的高階技巧
這篇文章主要介紹了SprinBoot如何集成參數(shù)校驗(yàn)Validator及參數(shù)校驗(yàn)的高階技巧包括自定義校驗(yàn)、分組校驗(yàn),本文分步驟給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05