使用mybatis的@Interceptor實現(xiàn)攔截sql的方法詳解
一 mybatis的攔截器
1.1 攔截器介紹
攔截器是一種基于 AOP(面向切面編程)的技術(shù),它可以在目標(biāo)對象的方法執(zhí)行前后插入自定義的邏輯。
1.2 語法介紹
1.注解@Intercepts
@Intercepts({@Signature(type = StatementHandler.class, method = “prepare”, args = {Connection.class, Integer.class})})
,表示在 SQL 執(zhí)行之前進(jìn)行攔截處理。
@Intercepts 的作用:聲明這是一個攔截器。
屬性:Signature(注解)
@Signature:要攔截的具體方法
? 屬性: type-攔截接口(四種類型 ),
method-攔截的方法(update,insert,select),
args-重載時根據(jù)參數(shù)列表確定要攔截的方法。
2.介紹:
Executor:攔截執(zhí)行器的方法,例如 update、query、commit、rollback 等。可以用來實現(xiàn)緩存、事務(wù)、分頁等功能。
ParameterHandler:攔截參數(shù)處理器的方法,例如 setParameters 等??梢杂脕磙D(zhuǎn)換或加密參數(shù)等功能。
ResultSetHandler:攔截結(jié)果集處理器的方法,例如 handleResultSets、handleOutputParameters 等??梢杂脕磙D(zhuǎn)換或過濾結(jié)果集等功能。
StatementHandler:攔截語句處理器的方法,例如 prepare、parameterize、batch、update、query 等。可以用來修改 SQL 語句、添加參數(shù)、記錄日志等功能。
1.3 API接口
1.intercept:主要是寫我們具體業(yè)務(wù)邏輯,比如針對增刪改sql語句添加更新日期。
2.plugin:生成代理對象
3.setProperties:設(shè)置攔截器屬性
二 實現(xiàn)案例
2.1 結(jié)構(gòu)
2.2 代碼
package com.ljf.springboot.mybaits.demos.utils; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.plugin.*; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.type.TypeHandlerRegistry; import org.springframework.stereotype.Component; import java.text.DateFormat; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Properties; /** * 1. 打印mysql完整的執(zhí)行語句 * 2. 打印mysql語句執(zhí)行時間 * 這里我們攔截Executor里面的query和update方法 */ @Component @Intercepts({ @Signature( method = "query", type = Executor.class, args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class} ), @Signature( type = Executor.class, method = "update", args = {MappedStatement.class, Object.class} ) }) public class LogInterceptor implements Interceptor { /** * 是否顯示語句的執(zhí)行時間 */ public static final String PROPERTIES_KEY_ENABLE_EXECUTOR_TIME = "enableExecutorTIme"; public static final String ENABLE_EXECUTOR_TIME = "0"; // 顯示 private boolean enableExecutorTime = false; @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println("dddddd======"); // 獲取執(zhí)行方法的MappedStatement參數(shù),不管是Executor的query方法還是update方法,第一個參數(shù)都是MappedStatement MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; Object parameter = null; if (invocation.getArgs().length > 1) { parameter = invocation.getArgs()[1]; } String sqlId = mappedStatement.getId(); BoundSql boundSql = mappedStatement.getBoundSql(parameter); Configuration configuration = mappedStatement.getConfiguration(); long sqlStartTime = System.currentTimeMillis(); Object re = invocation.proceed(); long sqlEndTime = System.currentTimeMillis(); // 打印mysql執(zhí)行語句 String sql = getSql(configuration, boundSql, sqlId); System.out.println(sql); // 打印mysql執(zhí)行時間 if (enableExecutorTime) { String sqlTimeLog = sqlId + " 方法對應(yīng)sql執(zhí)行時間:" + (sqlEndTime - sqlStartTime) + " ms"; System.out.println(sqlTimeLog); } return re; } /** * 通過該方法決定要返回的對象是目標(biāo)對象還是對應(yīng)的代理 * 不要想的太復(fù)雜,一般就兩種情況: * <p> * 1. return target; 直接返回目標(biāo)對象,相當(dāng)于當(dāng)前Interceptor沒起作用,不會調(diào)用上面的intercept()方法 * 2. return Plugin.wrap(target, this); 返回代理對象,會調(diào)用上面的intercept()方法 * * @param target 目標(biāo)對象 * @return 目標(biāo)對象或者代理對象 */ @Override public Object plugin(Object target) { System.out.println("==================dssssssssss"); return Plugin.wrap(target, this); } /** * 用于獲取在Configuration初始化當(dāng)前的Interceptor時時候設(shè)置的一些參數(shù) * * @param properties Properties參數(shù) */ @Override public void setProperties(Properties properties) { if (properties != null) { String executorTImeValue = properties.getProperty(PROPERTIES_KEY_ENABLE_EXECUTOR_TIME); if (executorTImeValue != null) { enableExecutorTime = executorTImeValue.equals(ENABLE_EXECUTOR_TIME); } } } private static String getSql(Configuration configuration, BoundSql boundSql, String sqlId) { return sqlId + " 方法對應(yīng)sql執(zhí)行語句:" + assembleSql(configuration, boundSql); } /** * 轉(zhuǎn)義正則特殊字符 ($()*+.[]?\^{} * \\需要第一個替換,否則replace方法替換時會有邏輯bug */ private static String makeQueryStringAllRegExp(String str) { if (str != null && !str.equals("")) { return str.replace("\\", "\\\\").replace("*", "\\*") .replace("+", "\\+").replace("|", "\\|") .replace("{", "\\{").replace("}", "\\}") .replace("(", "\\(").replace(")", "\\)") .replace("^", "\\^").replace("$", "\\$") .replace("[", "\\[").replace("]", "\\]") .replace("?", "\\?").replace(",", "\\,") .replace(".", "\\.").replace("&", "\\&"); } return str; } /** * 獲取參數(shù)對應(yīng)的string值 * * @param obj 參數(shù)對應(yīng)的值 * @return string */ private static String getParameterValue(Object obj) { String value; if (obj instanceof String) { value = "'" + obj.toString() + "'"; } else if (obj instanceof Date) { DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA); value = "'" + formatter.format(new Date()) + "'"; } else { if (obj != null) { value = obj.toString(); } else { value = ""; } } // 對特殊字符進(jìn)行轉(zhuǎn)義,方便之后處理替換 return value != null ? makeQueryStringAllRegExp(value) : value; } /** * 組裝完整的sql語句 -- 把對應(yīng)的參數(shù)都代入到sql語句里面 * * @param configuration Configuration * @param boundSql BoundSql * @return sql完整語句 */ private static String assembleSql(Configuration configuration, BoundSql boundSql) { // 獲取mapper里面方法上的參數(shù) Object sqlParameter = boundSql.getParameterObject(); // sql語句里面需要的參數(shù) -- 真實需要用到的參數(shù) 因為sqlParameter里面的每個參數(shù)不一定都會用到 List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); // sql原始語句(?還沒有替換成我們具體的參數(shù)) String sql = boundSql.getSql().replaceAll("[\\s]+", " "); if (parameterMappings.size() > 0 && sqlParameter != null) { // sql語句里面的?替換成真實的參數(shù) TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); if (typeHandlerRegistry.hasTypeHandler(sqlParameter.getClass())) { sql = sql.replaceFirst("\\?", getParameterValue(sqlParameter)); } else { MetaObject metaObject = configuration.newMetaObject(sqlParameter); for (ParameterMapping parameterMapping : parameterMappings) { // 一個一個把對應(yīng)的值替換進(jìn)去 按順序把?替換成對應(yīng)的值 String propertyName = parameterMapping.getProperty(); if (metaObject.hasGetter(propertyName)) { Object obj = metaObject.getValue(propertyName); sql = sql.replaceFirst("\\?", getParameterValue(obj)); } else if (boundSql.hasAdditionalParameter(propertyName)) { Object obj = boundSql.getAdditionalParameter(propertyName); sql = sql.replaceFirst("\\?", getParameterValue(obj)); } } } } return sql; } }
2.3 驗證效果
1.請求
2.日志結(jié)果
以上就是使用mybatis的@Interceptor實現(xiàn)攔截sql的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于mybatis @Interceptor攔截sql的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Springboot項目對數(shù)據(jù)庫用戶名密碼實現(xiàn)加密過程解析
這篇文章主要介紹了Springboot項目對數(shù)據(jù)庫用戶名密碼實現(xiàn)加密過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-06-06Java中eq、ne、ge、gt、le、lt的含義詳細(xì)解釋
Java中的比較運(yùn)算符包括eq(等于)、ne(不等于)、ge(大于或等于)、gt(大于)、le(小于或等于)和lt(小于),這些運(yùn)算符在控制流語句和條件語句中用于判斷條件是否滿足,從而決定程序的執(zhí)行路徑,需要的朋友可以參考下2024-11-11使用自定義注解進(jìn)行restful請求參數(shù)的校驗方式
這篇文章主要介紹了使用自定義注解進(jìn)行restful請求參數(shù)的校驗方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10關(guān)于JwtToken使用-重點(diǎn)看一下過期時間
這篇文章主要介紹了關(guān)于JwtToken使用-重點(diǎn)看一下過期時間,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07關(guān)于@SpringBootApplication詳解
這篇文章主要介紹了關(guān)于@SpringBootApplication的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08Java中SimpleDateFormat日期格式轉(zhuǎn)換詳解及代碼示例
這篇文章主要介紹了Java中SimpleDateFormat日期格式轉(zhuǎn)換詳解及代碼示例,具有一定借鑒價值,需要的朋友可以參考下。2017-12-12