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

  


Mybatis插件使用-统计sql执行时间的更多相关文章

  1. Mybatis插件Plugin

    Mybatis开源Plugin中最熟知的pagehelper,重点made in China 很多人开始用pagehelper时候,肯定很纳闷,以mysql为例,明明没有加limit语句,为什么打印出 ...

  2. MyBatis7:MyBatis插件及示例----打印每条SQL语句及其执行时间

    Plugins 摘一段来自MyBatis官方文档的文字. MyBatis允许你在某一点拦截已映射语句执行的调用.默认情况下,MyBatis允许使用插件来拦截方法调用 Executor(update.q ...

  3. MyBatis 插件 : 打印 SQL 及其执行时间

    Plugins 摘一段来自MyBatis官方文档的文字. MyBatis允许你在某一点拦截已映射语句执行的调用.默认情况下,MyBatis允许使用插件来拦截方法调用: Executor(update. ...

  4. MyBatis插件及示例----打印每条SQL语句及其执行时间

    Plugins 摘一段来自MyBatis官方文档的文字. MyBatis允许你在某一点拦截已映射语句执行的调用.默认情况下,MyBatis允许使用插件来拦截方法调用 Executor(update.q ...

  5. Mybatis插件,能做的事情真的很多

    大家好,我是架构摆渡人.这是实践经验系列的第九篇文章,这个系列会给大家分享很多在实际工作中有用的经验,如果有收获,还请分享给更多的朋友. Mybatis是我们经常用的一款操作数据库的框架,它的插件机制 ...

  6. 统计sql语句执行效率

    --统计sql语句执行效率SELECT (total_elapsed_time / execution_count)/1000 N'平均时间ms' ,total_elapsed_time/1000 N ...

  7. Mybatis插件机制以及PageHelper插件的原理

    首先现在已经有很多Mybatis源码分析的文章,之所以重复造轮子,只是为了督促自己更好的理解源码. 1.先看一段PageHelper拦截器的配置,在mybatis的配置文件<configurat ...

  8. 动手实践Mybatis插件

    前言 Mybatis的插件开发过程的前提是必须要对Mybatis整个SQL执行过程十分熟悉,这样才能正确覆盖源码保证插件运行,总的来说Mybatis的插件式一种侵入式插件,使用时应该十分注意. 在之前 ...

  9. mybatis插件机制

    目录 mybatis插件机制 主要 类/接口 和 方法 mybatis插件机制实现 mybatis插件机制 mybatis的插件机制使用动态代理实现,不了解的朋友请先了解代理模式和动态代理:插件本质是 ...

随机推荐

  1. Python自动化开发 - 面向对象(一)

    本节内容 1.编程范式 面向过程编程 面向对象编程 2.面向对象编程介绍 类的语法 类与实例内存分配 构造方法 自定义方法 3.面向对象特性 一.编程范式 编程是程序员 用特定的语法+数据结构+算法组 ...

  2. 调用DLL窗体-Delphi实例

    (一)通过向导DLL Wizard新建一个动态链接库,取名为:DLLPro.dpr.说明:当在DLL工程文件中使用了String类型时,要有 uses ShareMem ,不过建议使用PChar类型. ...

  3. Android-Java构造代码块&构造方法隐式三行

    构造代码块: 描述Teacher对象/实体: package android.java.oop06; public class Teacher { private int id = 007; priv ...

  4. Windows 下环境变量设置(命令行)

    0. 背景 查了一下 SETX 命令可以实现,先简单看了下帮助.以后再写一个实例. 1. SETX /?    输出内容: SetX 有三种使用方式: 语法 1: SETX [/S system [/ ...

  5. 基于Redis的CAS集群

    单点登录(SSO)是复杂应用系统的基本需求,Yale CAS是目前常用的开源解决方案.CAS认证中心,基于其特殊作用,自然会成为整个应用系统的核心,所有应用系统的认证工作,都将请求到CAS来完成.因此 ...

  6. Windows编译Opencv

    下载安装CMake 下载Opencv源码 打开CMake,设置源码路径和生成路径,点击Configure选择要生成的版本.(这里要多次Configure,直到所有红色消失!) 勾选BUILD_open ...

  7. wpf根据X与Y轴获取内部值

    效果图: 环境 Visual Studio ,.Net Framework 4.0 SDK 支持Windows XP,Windows Win7.. 实现方法: 创建ListBox设置ListBox.I ...

  8. Spring IOC 容器源码分析 - 循环依赖的解决办法

    1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...

  9. C标准库pow函数精度问题。

    #include <stdio.h> int main () { int temp,i; double a=2.4568; unsigned ]; ;i<;i++) { temp=( ...

  10. 【xsy2194】Philosopher set+线段树合并

    题目大意:给你一个长度为$n$的序列,有$m$次操作,每次操作是以下两种之一: 对某个区间内的数按照升序/降序排序,询问某个区间内数的积在十进制下首位数字是多少. 数据范围:$n,m≤2\times ...