mysql打印SQL语句
场景:在业务中,系统有时候mysql语句更新和变动,控制台看不到SQL语句信息,这时候需要把数据库交互的SQL语句打印在控制台;
处理此方法之一方案:
增加一个sql语句拦截器,拦截打印SQL语句变更信息;
此代码可以直接复用,新增一个拦截类;
package cn.com.fero.frame.base.interceptor; import java.text.DateFormat; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Properties; import java.util.regex.Matcher; import org.apache.ibatis.cache.CacheKey; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.plugin.*; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.type.TypeHandlerRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; @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, CacheKey.class, BoundSql.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})}) public class SqlStatementInterceptor implements Interceptor { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public Object intercept(Invocation invocation) throws Throwable { Object returnValue; long start = System.currentTimeMillis(); // 执行 SQL语句 returnValue = invocation.proceed(); long end = System.currentTimeMillis(); // 耗时 long time = end - start; try { final Object[] args = invocation.getArgs(); MappedStatement ms = (MappedStatement) args[0]; Object parameter = null; //获取参数,if语句成立,表示sql语句有参数,参数格式是map形式 if (args.length > 1) { parameter = invocation.getArgs()[1]; } // 获取到节点的 id,即 sql语句的 id String sqlId = ms.getId(); // BoundSql就是封装 MyBatis最终产生的 sql类 BoundSql boundSql = ms.getBoundSql(parameter); // 获取节点的配置 Configuration configuration = ms.getConfiguration(); // 获取到最终的 sql语句 printSql(configuration, boundSql, sqlId, time); } catch (Exception e) { logger.error("sql拦截异常:{} ", e.getMessage()); } return returnValue; } private void printSql(Configuration configuration, BoundSql boundSql, String sqlId, long time) { String sql = showSql(configuration, boundSql); logger.info("【SQL语句Id】>>>> {}", sqlId); logger.info("【SQL语句耗时】>>>> {} ms", time); logger.info("【SQL语句】>>>> {}", sql); } private static String getParameterValue(Object obj) { String value = null; if (obj instanceof String) { value = "'" + obj.toString() + "'"; } else if (obj instanceof Date) { DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA); value = "'" + formatter.format(obj) + "'"; } else { if (obj != null) { value = obj.toString(); } else { value = ""; } } return value; } private static String showSql(Configuration configuration, BoundSql boundSql) { // 获取参数 Object parameterObject = boundSql.getParameterObject(); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); String questionMark = "?"; // sql语句中多个空格都用一个空格代替 StringBuilder result = repaceWhiteSapce(boundSql.getSql()); if (!CollectionUtils.isEmpty(parameterMappings) && parameterObject != null) { // 获取类型处理器注册器,类型处理器的功能是进行java类型和数据库类型的转换 // 如果根据 parameterObject.getClass()可以找到对应的类型,则替换 TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { int index = result.indexOf(questionMark); if (index > -1){ result.replace(index, index + 1, Matcher.quoteReplacement(getParameterValue(parameterObject))); } } else { // MetaObject主要是封装了 originalObject对象,提供了 get和 set的方法用于获取和设置 originalObject的属性值 // 主要支持对 JavaBean、Collection、Map三种类型对象的操作 MetaObject metaObject = configuration.newMetaObject(parameterObject); for (ParameterMapping parameterMapping : parameterMappings) { String propertyName = parameterMapping.getProperty(); if (metaObject.hasGetter(propertyName)) { Object obj = metaObject.getValue(propertyName); int index = result.indexOf(questionMark); if (index > -1){ result.replace(index, index + 1, Matcher.quoteReplacement(getParameterValue(obj))); } } else if (boundSql.hasAdditionalParameter(propertyName)) { Object obj = boundSql.getAdditionalParameter(propertyName); // 该分支是动态 sql int index = result.indexOf(questionMark); if (index > -1){ result.replace(index, index + 1, Matcher.quoteReplacement(getParameterValue(obj))); } } else { int index = result.indexOf(questionMark); if (index > -1){ result.replace(index, index + 1, "缺失"); } } } } } return result.toString(); } public static StringBuilder repaceWhiteSapce(String original) { StringBuilder sb = new StringBuilder(); //标记是否是前一个空格,空格不继续append boolean isFirstSpace = false; // original = original.trim();//如果考虑开头和结尾有空格的情形 char c; for (int i = 0; i < original.length(); i++) { c = original.charAt(i); if (c == '\n'){ continue; } //遇到空格字符时,先判断是不是第一个空格字符 if (c == ' ' || c == '\t') { if (!isFirstSpace) { sb.append(c); isFirstSpace = true; } } else {//遇到非空格字符时 sb.append(c); isFirstSpace = false; } } return sb; } @Override public Object plugin(Object arg0) { return Plugin.wrap(arg0, this); } @Override public void setProperties(Properties properties) { // do nothing } }
在配置类中注入SQL打印拦截器;(MybatisPlusInterceptorConfig.java)
/** * 配置 sql打印拦截器 * @return */ @Bean public SqlStatementInterceptor sqlStatementInterceptor() { return new SqlStatementInterceptor(); }
以上配置完成,即可打印变动的SQL信息在控制台显示;