给SQL增加trace方便排查慢SQL

通过编写mybatis插件的方式,给SQL追加trace和statementId,方便跟踪慢SQL日志和对应的mapper,同时在日志配置文件里的 patternLayout 属性加入 %X{request-id}

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);

  }
}