背景介紹:最近由於產品數據量較大,sql執行十分低效,正在做數據庫優化,所以想在日志中看到每個sql執行的時間,以方便針對性的優化。
查找相關資料,了解到Mybatis有一款插件,是基於interceptor來實現的,可以在攔截器中來輸出每個sql的執行時間,配置方便且簡單,經過自測可用。
1、在dao的配置文件中,在sqlSessionFactory配置項里添加插件依賴:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="configLocation" value="classpath:mybatis-config.xml" /> <property name="mapperLocations" value="classpath:/sqlmap/*Mapper.xml" /> <property name="dataSource" ref="dataSource" /> <property name="plugins"> <array> <!-- 基於攔截器的實現,配置攔截器所在工程的全路徑 --> <bean id="sqlStatementInterceptor" class="com.**.interceptor.SqlStatementInterceptor"/> </array> </property> </bean>
2、在工程com.**.interceptor包路徑下添加SqlStatementInterceptor.java,具體代碼如下:
/** * Mybatis SQL攔截器,記錄每個sqlId對應的執行時間 * * @author yehaixiao * @date 2017-09-07 */ @Intercepts(value = { @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }), @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }), @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class }) }) public class SqlStatementInterceptor implements Interceptor { private static Logger logger = Logger.getLogger(SqlStatementInterceptor.class); @Override public Object intercept(Invocation invocation) { MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; // sqlId為mapper文件中定義的id,例如:com.**.dao.**Mapper.selectByPrimaryKey String sqlId = mappedStatement.getId(); // 開始時間 long start = System.currentTimeMillis(); try { return invocation.proceed(); } catch (Exception e) { logger.error(sqlId + "執行失敗!", e); return null; } finally { long end = System.currentTimeMillis(); long time = end - start; StringBuilder str = new StringBuilder(); str.append(sqlId); str.append(": "); str.append("cost time "); str.append(time); str.append(" ms."); String sqlInfo = str.toString(); logger.debug(sqlInfo); } } @Override public Object plugin(Object arg0) { return Plugin.wrap(arg0, this); } @Override public void setProperties(Properties arg0) { } }
說明:自定義的攔截器實現了org.apache.ibatis.plugin.Interceptor,Interceptor中有三個方法:
public abstract interface Interceptor { public abstract Object intercept(Invocation paramInvocation) throws Throwable; public abstract Object plugin(Object paramObject); public abstract void setProperties(Properties paramProperties); }
我們主要實現了intercept方法,入參是一個Invocation,Invocation的內部結構如下所示:
第一個參數是長度為4的args數組,數組的第一個參數是一個MappedStatement,其實這個就是執行一個sql對應的對象,該對象中有個getId,獲取到的結果就是mapper文件中sql定義的id,這樣就很容易明白自定義的攔截器里intercept方法里第一句話含義。
直到現在,相關的配置、代碼以及說明全都結束,在執行sql的時候,日志中可以看到如下輸出: