mybatis插件:打印sql及其执行时间实现方法

  

  

摘一段来自MyBatis官方文档的文字。

  

MyBatis允许你在某一点拦截已映射语句执行的调用。默认情况下,MyBatis允许使用插件来拦截方法调用:

  

执行人(更新、查询、flushStatements commint,回滚,getTransaction,关闭,关闭)

  

ParameterHandler (getParameterObject setParameters)

  

ResultSetHandler (handleResultSets handleOutputParameters)

  

StatementHandler(准备、参数化、批处理、更新、查询)

  

这些类中方法的详情可以通过查看每个方法的签名来发现,而且它们的源代码存在于MyBatis发行包中。你应该理解你所覆盖方法的行为,假设你所做的要比监视调用要多。如果你尝试修改或覆盖一个给定的方法,你可能会打破MyBatis的核心。这是低层次的类和方法,要谨慎使用插件。

  

  

以下通过代码来演示一下如何使用MyBatis的插件,要演示的场景是:打印每条真正执行的SQL语句及其执行的时间。这是一个非常有用的需求,MyBatis本身的日志可以记录SQL,但是有以下几个问题:

  

MyBatis日志打印出来的SQL日志,参数都被占位符“& # 63;替换,无法知道真正执行SQL语的句中的参数是什么

  

MyBatis日志打印出来的SQL日志,有大量的换行符,通常一句SQL语句要通过十几行显示,阅读体验非常差

  

无法记录SQL执行时间,有SQL执行时间就可以精准定位到执行时间比较慢的SQL

  

写MyBatis插件非常简单,只需要实现拦截器接口即可,我这里将我的拦截命名为SqlCostInterceptor:

     /* *   * Sql执行时间记录拦截器   */@Intercepts ({@Signature(类型=StatementHandler。类,方法="查询",arg游戏={语句。类、ResultHandler.class})   @Signature(类型=StatementHandler。类,方法=案隆?arg游戏={Statement.class}),   @Signature(类型=StatementHandler。类,方法="批",arg游戏={语句。类})})   公共类SqlCostInterceptor实现拦截器{      @Override   公共对象拦截(调用调用)抛出Throwable {   对象目标=invocation.getTarget ();      长时间的开始时间=System.currentTimeMillis ();   StatementHandler StatementHandler=(StatementHandler)的目标;   尝试{   返回invocation.proceed ();   最后}{   长endTime=System.currentTimeMillis ();   长sqlCost=endTime -开始时间;      BoundSql BoundSql=statementHandler.getBoundSql ();   字符串sql=boundSql.getSql ();   对象parameterObject=boundSql.getParameterObject ();   ListparameterMappingList=boundSql.getParameterMappings ();//格式化Sql语句,去除换行符,替换参数   sql=formatSql (sql、parameterObject parameterMappingList);      system . out。println (“SQL: [" + SQL + "]执行耗时”+ sqlCost +“女士”);   }   }      @Override   公共插件对象(对象目标){   返回插件。包装(目标,这个);   }      @Override   公共空间找(属性属性){      }      @SuppressWarnings (“unchecked”)   私人字符串formatSql (sql字符串、对象parameterObject ListparameterMappingList) {//输入sql字符串空判断   如果(sql==null | | sql.length ()==0) {   返回";   }//美化sql   sql=beautifySql (sql);//不传参数的场景,直接把Sql美化一下返回出去   如果(parameterObject==null | | parameterMappingList==null | | parameterMappingList.size ()==0) {   返回sql;   }//定义一个没有替换过占位符的sql,用于出异常时返回   字符串sqlWithoutReplacePlaceholder=sql;      尝试{   如果(parameterMappingList !=null) {   Class<& # 63;比;parameterObjectClass=parameterObject.getClass ();//如果参数是StrictMap且价值类型为收集、获取关键=傲斜怼钡氖粜?这里主要是为了处理& lt; foreach>循环时传入列表这种参数的占位符替换//例如select *从xxx id & lt; foreach收集=傲斜怼弊4恰? lt;/foreach>   如果(isStrictMap (parameterObjectClass)) {   StrictMap & lt; delete>, & lt; update> & lt; select>时传入parameterType为地图的场景   Map<& # 63; & # 63;比;paramMap=(Map<& # 63; & # 63;祝辞)parameterObject;   sql=handleMapParameter (sql、paramMap parameterMappingList);   其他}{//通用场景,比如传的是一个自定义的对象或者八种基本数据类型之一或者字符串   sql=handleCommonParameter (sql、parameterMappingList parameterObjectClass parameterObject);   }   }   }捕捉(异常e) {//占位符替换过程中出现异常,则返回没有替换过占位符但是格式美化过的sql,这样至少保证sql语句比BoundSql中的sql更好看   返回sqlWithoutReplacePlaceholder;   }      返回sql;   }/* *   *美化Sql   */私人beautifySql字符串(字符串sql) {   sql=sql。替换(" \ n ", " ")。替换(“\ t ", " ")。替换(" "," ")。替换(“(”、“(”)。替换(“)”、“)”)。替换(”、“,”、“);      返回sql;   }/* *   *处理参数为列表的场景   */私人handleListParameter字符串(字符串sql, Collection

mybatis插件:打印sql及其执行时间实现方法