package com.xzx.gc.interceptor; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import com.xzx.gc.common.dto.log.SqlDto; import com.xzx.gc.common.utils.SpringUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; 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.mapping.SqlCommandType; 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.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Properties; /**ALL < index < range ~ index_merge < ref < eq_ref < const < system * 1.可以用来分析SQL执行效率 * 2.可以用来获取实际执行的SQL * * @author zhanjixun * @time 2018年6月28日 19:20:23 */ @Slf4j @Intercepts({ @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}), @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})} ) @Component public class SqlInterceptor implements Interceptor { public static final String SQL_PARAM="sqlParam"; @Autowired private SpringUtil springUtil; private int MIN_SIZE = 0; @Override public Object intercept(Invocation invocation) throws Throwable { HttpServletRequest request = springUtil.getCurrentRequest(); List sqlList=null; if(request!=null) { sqlList = (List) request.getAttribute(SQL_PARAM); } Object[] args = invocation.getArgs(); MappedStatement mappedStatement = (MappedStatement) args[0]; BoundSql boundSql=null; if (args.length > 5) { boundSql = (BoundSql) args[5]; } if(boundSql==null){ Object parameter= args[1]; boundSql = mappedStatement.getBoundSql(parameter); } String sqlId = mappedStatement.getId(); Configuration configuration = mappedStatement.getConfiguration(); long startTime = System.currentTimeMillis(); Object result = null; try { result = invocation.proceed(); } finally { try { if(request!=null) { SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); int effectNum = 0; if (sqlCommandType == SqlCommandType.UPDATE || sqlCommandType == SqlCommandType.INSERT || sqlCommandType == SqlCommandType.DELETE) { effectNum = Convert.toInt(result, 0); }else if(sqlId.endsWith("_COUNT")){ List objects = Convert.toList(result); if(CollUtil.isNotEmpty(objects)){ effectNum=Convert.toInt(objects.get(0),0); } }else if(sqlCommandType==SqlCommandType.SELECT){ List objects = Convert.toList(result); if(CollUtil.isNotEmpty(objects)){ effectNum=objects.size(); } } long sqlCostTime = System.currentTimeMillis() - startTime; String sql = getSql(configuration, boundSql); SqlDto sqlDto = new SqlDto(); sqlDto.setSql(sql); sqlDto.setSqlId(sqlId); sqlDto.setCostTime(sqlCostTime + ""); sqlDto.setEffectNum(effectNum + ""); sqlDto.setSqlType(sqlCommandType.name()); if (CollUtil.isNotEmpty(sqlList)) { sqlList.add(sqlDto); } else { sqlList = CollUtil.newArrayList(sqlDto); } request.setAttribute(SQL_PARAM, sqlList); } } catch (Exception ignored) { // ignored.printStackTrace(); } } return result; } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { if (properties == null) { return; } if (properties.containsKey("minLogSize")) { MIN_SIZE = Integer.valueOf(properties.getProperty("minLogSize")); } } private String getSql(Configuration configuration, BoundSql boundSql) { // 输入sql字符串空判断 if(boundSql==null){ return ""; } String sql = boundSql.getSql(); if (StringUtils.isBlank(sql)) { return ""; } //美化sql sql = beautifySql(sql); //填充占位符, 目前基本不用mybatis存储过程调用,故此处不做考虑 Object parameterObject = boundSql.getParameterObject(); List parameterMappings = boundSql.getParameterMappings(); if (!parameterMappings.isEmpty() && parameterObject != null) { TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { sql = this.replacePlaceholder(sql, parameterObject); } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); for (ParameterMapping parameterMapping : parameterMappings) { String propertyName = parameterMapping.getProperty(); if (metaObject.hasGetter(propertyName)) { Object obj = metaObject.getValue(propertyName); sql = replacePlaceholder(sql, obj); } else if (boundSql.hasAdditionalParameter(propertyName)) { Object obj = boundSql.getAdditionalParameter(propertyName); sql = replacePlaceholder(sql, obj); } } } } return sql; } private String beautifySql(String sql) { return sql.replaceAll("[\\s\n ]+", " "); } private String replacePlaceholder(String sql, Object parameterObject) { String result; if(parameterObject!=null) { if (parameterObject instanceof String) { result = "'" + parameterObject.toString() + "'"; } else if (parameterObject instanceof Date) { result = "'" + getDate2String((Date) parameterObject) + "'"; } else { result = parameterObject.toString(); } return sql.replaceFirst("\\?", result); }else { return sql; } } private String getDate2String(Date parameterObject) { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(parameterObject); } }