1、攔截器應用場景:
(1)分頁,如com.github.pagehelper的分頁插件實現。
(2)攔截sql做日志監控;
(3)統一對某些sql進行統一條件拼接,類似於分頁。
2、研究初衷:
(1)做什么:通過攔截器對查詢的 sql 進行改寫, 讓 pageHelper 執行的是改寫后的 sql。
(2)怎樣做:由於分頁使用的是 Pagehelper ,其內部機制也是通過攔截器實現的。基於 MyBatis 攔截器鏈的加載機制,后加載的會先執行,也就是說我們
自定義的攔截器,必須加載在 Pagehelper之后。
3、相關配置類實現:
(1)MySqlInterceptor 類:
package spcommon.config; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.*; import org.apache.ibatis.plugin.*; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Properties; /** * 自定義 MyBatis 攔截器 */ @Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class,ResultHandler.class})}) public class MySqlInterceptor implements Interceptor { private static final Logger logger= LoggerFactory.getLogger(MySqlInterceptor.class); /** * intercept 方法用來對攔截的sql進行具體的操作 * @param invocation * @return * @throws Throwable */ @Override public Object intercept(Invocation invocation) throws Throwable { logger.info("執行intercept方法:{}", invocation.toString()); Object[] args = invocation.getArgs(); MappedStatement ms = (MappedStatement) args[0]; Object parameterObject = args[1]; // id為執行的mapper方法的全路徑名,如com.mapper.UserMapper String id = ms.getId(); // sql語句類型 select、delete、insert、update String sqlCommandType = ms.getSqlCommandType().toString(); // 僅攔截 select 查詢 //if (!sqlCommandType.equals(SqlCommandType.SELECT.toString())) { // return invocation.proceed(); //} BoundSql boundSql = ms.getBoundSql(parameterObject); String origSql = boundSql.getSql(); logger.info("原始SQL: {}", origSql); // 組裝新的 sql String newSql = origSql + " limit 1"; // 重新new一個查詢語句對象 BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), newSql, boundSql.getParameterMappings(), boundSql.getParameterObject()); // 把新的查詢放到statement里 MappedStatement newMs = newMappedStatement(ms, new BoundSqlSqlSource(newBoundSql)); for (ParameterMapping mapping : boundSql.getParameterMappings()) { String prop = mapping.getProperty(); if (boundSql.hasAdditionalParameter(prop)) { newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop)); } } Object[] queryArgs = invocation.getArgs(); queryArgs[0] = newMs; logger.info("改寫的SQL: {}", newSql); return invocation.proceed(); } /** * 定義一個內部輔助類,作用是包裝 SQL */ class BoundSqlSqlSource implements SqlSource { private BoundSql boundSql; public BoundSqlSqlSource(BoundSql boundSql) { this.boundSql = boundSql; } public BoundSql getBoundSql(Object parameterObject) { return boundSql; } } private MappedStatement newMappedStatement (MappedStatement ms, SqlSource newSqlSource) { MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType()); builder.resource(ms.getResource()); builder.fetchSize(ms.getFetchSize()); builder.statementType(ms.getStatementType()); builder.keyGenerator(ms.getKeyGenerator()); if (ms.getKeyProperties() != null && ms.getKeyProperties().length > 0) { builder.keyProperty(ms.getKeyProperties()[0]); } builder.timeout(ms.getTimeout()); builder.parameterMap(ms.getParameterMap()); builder.resultMaps(ms.getResultMaps()); builder.resultSetType(ms.getResultSetType()); builder.cache(ms.getCache()); builder.flushCacheRequired(ms.isFlushCacheRequired()); builder.useCache(ms.isUseCache()); return builder.build(); } @Override public Object plugin(Object target) { logger.info("plugin方法:{}", target); if (target instanceof Executor) { return Plugin.wrap(target, this); } return target; } @Override public void setProperties(Properties properties) { // 獲取屬性 // String value1 = properties.getProperty("prop1"); logger.info("properties方法:{}", properties.toString()); } }
(2)MyBatisConfig 類:
package spcommon.config; import com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration; import org.apache.ibatis.session.SqlSessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; import java.util.List; import java.util.Properties; @Configuration @AutoConfigureAfter(PageHelperAutoConfiguration.class) public class MyBatisConfig { @Autowired private List<SqlSessionFactory> sqlSessionFactoryList; @PostConstruct public void addMySqlInterceptor() { MySqlInterceptor interceptor = new MySqlInterceptor(); for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) { // 添加自定義屬性 // Properties properties = new Properties(); // properties.setProperty("prop1", "value1"); // interceptor.setProperties(properties); sqlSessionFactory.getConfiguration().addInterceptor(interceptor); } } }
(3)在 resources 目錄下創建META-INF目錄下,在 META-INF 目錄下創建 spring.factories 文件,文件內容如下:
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration,\ xxx.xxx.MyBatisConfig
4、測試總結
在同時使用 PageHelper 和 自定義攔截器時,以上配置可解決 spring boot 在使用 pagehelper-spring-boot-starter 后 ,自定義攔截器失效的問題。
參考鏈接:
https://www.jianshu.com/p/59e28ad9e738
https://www.jianshu.com/p/0a72bb1f6a21