当前位置: 代码网 > it编程>编程语言>Java > MyBatis-Plus批量操作SQL日志不打印问题的解决方案

MyBatis-Plus批量操作SQL日志不打印问题的解决方案

2026年03月05日 Java 我要评论
问题描述在使用 mybatis-plus 的savebatch()和updatebatchbyid()方法进行批量数据操作时,发现自定义的 druid sql 日志拦截器(sqllogintercep

问题描述

在使用 mybatis-plus 的 savebatch() 和 updatebatchbyid() 方法进行批量数据操作时,发现自定义的 druid sql 日志拦截器(sqlloginterceptor)无法打印这些批量操作的 sql 语句,导致调试和问题排查困难。

问题分析

1. 批量操作的特殊性

mybatis-plus 的批量操作方法(如 savebatchupdatebatchbyid)使用了 jdbc 的批处理(batch)模式来提高性能。批处理模式与普通的单条 sql 执行方式不同:

  • 普通执行:每条 sql 单独执行,会触发 statementexecuteafterstatementexecuteupdateafter 等方法
  • 批处理执行:多条 sql 一起提交执行,只会触发 statementexecutebatchafter 方法

2. 原有拦截器的问题

原有的 sqlloginterceptor 继承自 druid 的 filtereventadapter,实现了以下方法:

@override
protected void statementexecuteafter(statementproxy statement, string sql, boolean firstresult) {
    statement.setlastexecutetimenano();
    printsqllog(statement, sql);
}

@override
protected void statementexecuteupdateafter(statementproxy statement, string sql, int updatecount) {
    statement.setlastexecutetimenano();
    printsqllog(statement, sql);
}

@override
protected void statementexecutebatchafter(statementproxy statement, int[] result) {
    statement.setlastexecutetimenano();
    // 这里没有打印日志!
}

可以看到,statementexecutebatchafter 方法中只设置了执行时间,但没有打印 sql 日志,这就是批量操作 sql 不打印的根本原因。

解决方案

方案一:修改 druid 拦截器(推荐)

修改 sqlloginterceptor.java 的 statementexecutebatchafter 方法,添加 sql 日志打印逻辑:

@override
protected void statementexecutebatchafter(statementproxy statement, int[] result) {
    statement.setlastexecutetimenano();
    // 批量执行后也打印sql日志
    string sql = statement.getbatchsql();
    if (stringutil.isnotblank(sql)) {
        printsqllog(statement, sql);
    } else {
        // 如果批量sql为空,尝试获取最后执行的sql
        sql = statement.getlastexecutesql();
        if (stringutil.isnotblank(sql)) {
            printsqllog(statement, sql);
        }
    }
}

优点

  • 统一的日志格式
  • 包含执行时间统计
  • 可以格式化 sql 和参数

缺点

  • 批量操作可能只显示一条 sql 模板,看不到每条具体的参数

方案二:启用 mybatis 原生日志

在 logback.xml 配置文件中添加 mybatis mapper 的 debug 级别日志:

<!-- mybatis sql 日志 -->
<logger name="com.hzys.mapper" level="debug"/>

优点

  • 可以看到每条 sql 的详细参数
  • mybatis 原生支持,稳定可靠

缺点

  • 日志格式与自定义拦截器不一致
  • 日志量较大

方案三:双管齐下(最佳实践)

同时使用方案一和方案二,既能保证批量操作的 sql 被记录,又能在需要时查看详细的参数信息。

完整代码示例

1. 修改后的 sqlloginterceptor

@slf4j
public class sqlloginterceptor extends filtereventadapter {
    private static final sqlutils.formatoption format_option = new sqlutils.formatoption(false, false);

    @override
    protected void statementexecutebatchafter(statementproxy statement, int[] result) {
        statement.setlastexecutetimenano();
        // 批量执行后也打印sql日志
        string sql = statement.getbatchsql();
        if (stringutil.isnotblank(sql)) {
            printsqllog(statement, sql);
        } else {
            // 如果批量sql为空,尝试获取最后执行的sql
            sql = statement.getlastexecutesql();
            if (stringutil.isnotblank(sql)) {
                printsqllog(statement, sql);
            }
        }
    }

    private void printsqllog(statementproxy statement, string sql) {
        if (!log.isinfoenabled() || stringutil.isempty(sql)) {
            return;
        }

        try {
            // 获取参数
            int parameterssize = statement.getparameterssize();
            list<object> parameters = new arraylist<>(parameterssize);
            for (int i = 0; i < parameterssize; ++i) {
                parameters.add(getjdbcparameter(statement.getparameter(i)));
            }

            // 格式化sql
            string dbtype = statement.getconnectionproxy().getdirectdatasource().getdbtype();
            string formattedsql = sqlutils.format(sql, dbtype.of(dbtype), parameters, format_option);

            // 打印日志
            printsql(formattedsql, statement);
        } catch (exception e) {
            log.error("sql 格式化失败", e);
            log.info("\n\n==============  sql start  ==============\n" +
                    "execute sql : {}\n" +
                    "execute time: {}\n" +
                    "==============  sql  end   ==============\n",
                    sql, stringutil.format(statement.getlastexecutetimenano()));
        }
    }

    private static void printsql(string sql, statementproxy statement) {
        string sqllogger = "\n\n==============  sql start  ==============" +
                "\nexecute sql : {}" +
                "\nexecute time: {}" +
                "\n==============  sql  end   ==============\n";
        log.info(sqllogger, sql.trim(), stringutil.format(statement.getlastexecutetimenano()));
    }
}

2. logback 配置

<?xml version="1.0" encoding="utf-8"?>
<configuration scan="true" scanperiod="60 seconds">
    <!-- 其他配置... -->

    <!-- 日志输出级别 -->
    <root level="info">
        <appender-ref ref="stdout"/>
        <appender-ref ref="info"/>
        <appender-ref ref="warn"/>
        <appender-ref ref="error"/>
    </root>

    <!-- mybatis sql 日志 -->
    <logger name="com.hzys.mapper" level="debug"/>
    
    <logger name="net.sf.ehcache" level="info"/>
    <logger name="druid.sql" level="info"/>
</configuration>

3. druid 配置

@slf4j
@configuration
public class druidconfig {

    @bean
    @primary
    @configurationproperties("spring.datasource.druid")
    public datasource datasource() {
        druiddatasource datasource = druiddatasourcebuilder.create().build();

        // 添加自定义的sql日志拦截器
        sqlloginterceptor sqlloginterceptor = new sqlloginterceptor();
        datasource.getproxyfilters().add(sqlloginterceptor);

        return datasource;
    }
}

验证效果

修改后,执行批量操作时会看到类似以下的日志输出:

==============  sql start  ==============
execute sql : insert into employee_hourly_rate (id, project_member, member_level, ...) values (?, ?, ?, ...)
execute time: 15ms
==============  sql  end   ==============

==>  preparing: insert into employee_hourly_rate (id, project_member, member_level, ...) values (?, ?, ?, ...)
==> parameters: 1(long), 张三(string), 高级工程师(string), ...
==> parameters: 2(long), 李四(string), 中级工程师(string), ...
<==    updates: 2

注意事项

  1. 性能考虑:debug 级别的 mybatis 日志会输出大量信息,生产环境建议关闭或设置为 info 级别
  2. 日志过滤:可以在 sqlloginterceptor 中添加过滤逻辑,避免打印某些不需要的 sql(如健康检查)
  3. 批量大小:mybatis-plus 默认批量大小为 1000,可以通过配置调整
  4. 事务管理:批量操作需要在事务中执行,确保添加 @transactional 注解

总结

mybatis-plus 批量操作 sql 不打印的问题主要是因为批处理模式使用了不同的执行路径,原有的拦截器没有处理 statementexecutebatchafter 方法。通过修改拦截器并配合 mybatis 原生日志,可以完美解决这个问题,既能保证日志的完整性,又能在需要时查看详细的执行信息。

以上就是mybatis-plus批量操作sql日志不打印问题的解决方案的详细内容,更多关于mybatis-plus操作sql日志不打印的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com