sql执行语句完整打印方案(参数替换?)

引言

    打印执行的sql语句,有利于我们及时排查错误,而且mybatis也支持sql语句的打印,如果日志采用logback,只需要logback配置里配置一个logger name指向你项目dao包路径即可。类似于下面这种格式。

<logger name="com.x.x.dao.XxxDao" level="DEBUG" />

    虽然sql语句可以成功打印出来,但它把一个完整的sql拆分成两部分显示,boundSql和参数,当我们参数很多的时候,想拷贝sql去数据库跑一下就很麻烦。所以说打印一个完整的一复制就能跑的sql还是很重要的。

两种方案


方案1:利用idea的插件

    如果你使用的软件是idea的话,方案1就是可行的。可以去plugins下载插件 MyBatis Log Plugin,插件是免费的,其实这种方案也有局限性,就是日志要输出在控制台里才可以。安装完成后可以在Tools中找到。首先看下控制台sql的输出格式。
第一行---->Preparing---->sql语句
第二行---->Paramters---->参数
第三行---->Total
在这里插入图片描述
    接下来看下MyBatis Log这个插件里的输出格式。
在这里插入图片描述
    简洁明了,直接输出的就是替换了?的完整可执行sql语句,这种方案虽然很简单并且可以打印出完整sql语句,但是局限性很大,不好进行持久化操作,一般都会将sql执行写入日志中,接下来看下方案二。


方案2:实现一个拦截器来进行sql语句拼接并打印

    这种方案比较灵活,可以在代码中获得完整的sql,然后指定日志输出就好了,直接上代码来进行分析。

@Intercepts({
        @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 }) })

public class ExecteSqlLogInterceptor implements Interceptor {

    private static Logger daoLogger = LoggerFactory.getLogger("xxx");

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object parameter = null;
        if (invocation.getArgs().length > 1) {
            parameter = invocation.getArgs()[1];
        }
        BoundSql boundSql = mappedStatement.getBoundSql(parameter);
        Configuration configuration = mappedStatement.getConfiguration();
        String sqlId = mappedStatement.getId();
        int index = sqlId.indexOf("mapper");
        if(index != -1){
            sqlId = sqlId.substring(index+7);
        }
        long start = System.currentTimeMillis();

        Object returnValue = invocation.proceed();

        long end = System.currentTimeMillis();

        long time = end - start;
        if(time > 1){
            String exexcSql = getSql(configuration, sqlId, time ,boundSql);
            //System.out.println(exexcSql);
            daoLogger.info(exexcSql);
        }
        return returnValue;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    private static String getRuntimeExeSql(Configuration configuration, BoundSql boundSql) {
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        if (parameterMappings.size() > 0 && parameterObject != null) {
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                sql = sql.replaceFirst("\\?", getParameterValue(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 = sql.replaceFirst("\\?", getParameterValue(obj));
                    } else if (boundSql.hasAdditionalParameter(propertyName)) {
                        Object obj = boundSql.getAdditionalParameter(propertyName);
                        sql = sql.replaceFirst("\\?", getParameterValue(obj));
                    }
                }
            }
        }
        return 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(new Date()) + "'";
        } else {
            if (obj != null) {
                value = obj.toString();
            } else {
                value = "";
            }
        }
        return value;
    }

    private String getSql(Configuration configuration, String sqlId, long time, BoundSql boundSql){
        String sql = getRuntimeExeSql(configuration, boundSql);
        StringBuilder result = new StringBuilder();
        result.append(" ["+sqlId+"] ");
        result.append(" ==> ");
        result.append(sql);
        result.append(" ==> ");
        result.append(time);
        result.append("ms");
        return result.toString();
    }

    首先先看下@Intercepts这里定义成了一个拦截器,拦截了query,update操作,然后下面的daoLogger是日志输出的配置,xxx代表你logback里配置的loggerName,可以下面配置appender进行细化配置,日志的输出配置不多述了。主要看下我们能从invocation这个对象得到哪些属性。
在这里插入图片描述

    主要的参数是从args里拿出来,而且带?的sql语句可以从rootSqlNode获得,id是方法的全限定名。
在这里插入图片描述下面的MapperMethod$ParamMap这里就是参数,最后就是分页参数。由三部分组成。

BoundSql boundSql = mappedStatement.getBoundSql(parameter);这里得到带?的sql语句,主要替换操作是在getRuntimeExeSql这个函数里实现,把每一个?对应的参数拿出来,看一眼代码就懂作用了,不多解释了。得到之后logger.info打印到日志就ok了。其实可以直接打个断点进去,然后看看对应的参数,调用过程。然后记得要在mybatis里面加入这个拦截器。

    相对于第一种方案来说,第二种麻烦了很多,但第二种灵活,可以把sql输出到日志并且输出一些你想要的信息,比如运行时间之类。


To live is to function --xxy专用小尾巴。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值