Mybatis插件使用-统计sql执行时间

  背景介绍:最近由于产品数据量较大,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的时候,日志中可以看到如下输出:  

 

  

posted @ 2017-09-07 15:22  yehx  阅读(4297)  评论(0编辑  收藏  举报