给SQL增加trace方便排查慢SQL
通过编写mybatis插件的方式,给SQL追加trace和statementId,方便跟踪慢SQL日志和对应的mapper,同时在日志配置文件里的 patternLayout
属性加入 %X{request-id}
java
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.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
@Component
@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}),
}
)
public class MyPlugin implements Interceptor {
private static final String TRACE_ID = "request-id";
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();
MappedStatement mappedStatement = (MappedStatement) args[0];
Object parameter = args[1];
RowBounds rowBounds = (RowBounds) args[2];
ResultHandler resultHandler = (ResultHandler) args[3];
Executor executor = (Executor) invocation.getTarget();
CacheKey cacheKey;
BoundSql boundSql;
//由于逻辑关系,只会进入一次
if (args.length == 4) {
//4 个参数时
boundSql = mappedStatement.getBoundSql(parameter);
cacheKey = executor.createCacheKey(mappedStatement, parameter, rowBounds, boundSql);
} else {
//6 个参数时
cacheKey = (CacheKey) args[4];
boundSql = (BoundSql) args[5];
}
//id为执行的mapper方法的全路径名,如com.metro.dao.UserMapper.insertUser
String statementId = mappedStatement.getId();
//获取到原始sql语句
String sql = boundSql.getSql();
String traceId = MDC.get("TRACE_ID");
String newSql = "/*" + traceId + ";" + statementId + "*/" + sql; //修改后的sql
//通过反射修改boundSql对象的sql语句
Field field = boundSql.getClass().getDeclaredField("sql");
field.setAccessible(true);
field.set(boundSql, newSql);
//执行修改后的sql语句
return executor.query(mappedStatement, parameter, rowBounds, resultHandler, cacheKey, boundSql);
}
}