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专用小尾巴。

<think>嗯,用户想了解如何配置MyBatisPlus插件来打印SQL执行日志。我需要先回忆一下MyBatisPlus的相关配置方法。根据之前看到的引用内容,用户提供的引用里提到了几种方法,比如通过配置文件设置log-impl,使用logging.level调整日志级别,或者使用P6Spy插件。 首先,方法一是修改application.yml文件,配置mybatis-plus的log-impl为StdOutImpl。这样可以直接在控制台输出SQL日志。这个方法是比较直接的,引用[1]和引用[2]都提到了这种配置方式,应该可靠。 方法二是通过设置日志级别,将mapper接口所在的包的日志级别设为debug。引用[2]中的第二种方法就是这样的,配置logging.level.com.app.mapper为debug。不过用户需要替换成自己的mapper包路径,比如com.example.mapper。这可能依赖于项目使用的日志框架,比如Logback或Log4j2,但通常Spring Boot的配置是统一的。 第三种方法是使用P6Spy插件,引用[3]和引用[4]详细说明了如何配置spy.properties文件。需要添加依赖,然后创建配置文件,并指定相关参数,比如driverlist和logMessageFormat。这种方法可以输出更详细的日志,包括SQL执行时间,适合性能分析。但步骤稍微复杂,需要添加额外的依赖和配置文件。 另外,引用[5]提到了性能分析插件PerformanceInterceptor,不过这个插件在较新版本的MyBatisPlus中可能已经被标记为过时,或者被其他方法替代。所以可能不建议用户使用,但可以作为备选方案提及,同时提醒版本兼容性问题。 用户的问题是关于配置插件来打印SQL日志,所以需要分点列出不同的方法,每种方法的步骤和注意事项。比如方法一和方法二比较简单,适合快速启用;方法三适合需要详细日志的情况。同时,要提到依赖的添加,比如P6Spy需要引入对应的starter。 可能用户对这些配置的具体细节不太清楚,比如如何修改yml文件,或者如何添加依赖。需要给出具体的配置示例,比如代码块中的内容,让用户可以直接复制粘贴。同时,注意提示可能的坑,比如P6Spy配置中的driverlist必须指定真实的JDBC驱动,否则无法正常工作。 最后,生成相关问题,可能涉及这些方法的优缺点,如何选择,或者日志分析、性能优化等。确保问题覆盖用户可能感兴趣的后续步骤。</think>### MyBatis-Plus配置SQL执行日志打印方法 以下是三种常用方法实现SQL日志打印,根据项目需求选择合适方案: #### 方法1:通过YML配置直接输出SQL(推荐) 在`application.yml`中添加配置: ```yaml mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl ``` *特点*:无需额外依赖,控制台直接输出完整SQL参数,但缺少执行时间统计[^1][^2]。 #### 方法2:通过日志级别控制 在`application.yml`中配置mapper包路径日志级别: ```yaml logging: level: com.yourpackage.mapper: debug ``` *特点*:结合SLF4J日志框架使用,需确保已配置日志实现(如Logback),输出格式依赖日志框架配置[^2]。 #### 方法3:使用P6Spy性能分析插件(生产环境慎用) 1. 添加依赖: ```xml <dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId> <version>3.9.1</version> </dependency> ``` 2. 创建`spy.properties`文件: ```properties modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory driverlist=com.mysql.cj.jdbc.Driver logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger outagedetection=true outagedetectioninterval=2 ``` *特点*:可记录SQL执行时间、格式化输出语句,但会降低性能[^3][^4]。 #### 方法4(旧版):性能分析插件(已过时) ```java @Bean public PerformanceInterceptor performanceInterceptor() { return new PerformanceInterceptor(); } ``` *注意*:适用于MyBatis-Plus 3.4以下版本,新版建议使用P6Spy[^5]。 --- ### 配置建议对比表 | 方法 | 性能影响 | 输出信息 | 版本兼容性 | |------|----------|----------|------------| | YML配置 | 低 | 基础SQL | 全版本 | | 日志级别 | 低 | 基础SQL | 全版本 | | P6Spy | 较高 | 带耗时统计 | MP 3.3+ | | 性能插件 | 中 | 执行时间 | MP 3.4前 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值