最终解决方案在文末,可直接下滑到底部获取源码。

前言

在使用mybatis框架时,控制台默认输出的SQL语句时,是预编译的SQL语句,参数使用"?"代替。

Preparing : 
INSERT INTO sys_log ( ID, log_type, log_content, METHOD, operate_type, request_param, ip, userid, username, cost_time, create_time )
VALUES
	( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )

这种格式对于我们测试SQL是极其不方便的,于是我使用MybatisCodeHelper这个插件来自动生成完整的SQL语句,这个插件会在右侧展示一个侧栏,来显示系统运行的SQL,同时右键可直接跳转到SQL对应的xml文件,可以说是非常方便。

BUG现象

这时我发现一个很严重的问题,如果你的mybatis使用的是默认输出格式,即mapper格式,那么是可以正常的读取到对应的xml文件并跳转,但是如果你的mybatis使用的是SqlSession格式,那么插件无法读取到xml的路径,也就无法跳转。

于是我反手选择加入插件官方群,并询问群主如何解决。(特别鸣谢群主无偿热心解答,同时也强烈大家推荐使用MybatisCodeHelper插件来提升开发效率)

群主给出的答案是,只要preparing前显示类名就能识别。然后又很贴心的给出了他的测试图。我对比发现,我的控制台的preparing字段前确实是没有类名。

于是我就开始了经典又漫长的查阅资料的步骤。

排查过程

首先从logback开始排除,按照网上解答最多的方式,是在logback的配置文件的输出格式中添加%c来打印当前类的信息,于是我照做,启动后发现确实是打印了类信息,但是是在除了SQL语句以外的日志上打印,唯独我最需要的SQL语句前没有打印类信息。

于是再次转动大脑,既然添加%c确实更改了控制台打印信息,那说明这个配置文件时起作用了的,只不过SQL语句的控制权并不在logback手里。于是我再次审视控制台中打印的信息,并将目光锁定在了Creating a new session上,每次查询前,都会有这句话,于是我开始搜索这句话,查询到了一个关键信息:

所以说SqlSession中输出的日志是由org.mybatis.spring.SqlSessionUtils.getSqlSession类来打印的,而这个类并没有给我们预留重写的接口。因此,此路不通。

灵光突现

正当我准备放弃的时候,我又看到了另一篇CSDN上的文章,这也是最终给我提供解决思路的最重要的一篇文章:mybatis-plus配置控制台打印完整带参数SQL语句

这篇文章提醒了我,虽然logback无法直接控制输出的SqlSession中的格式,但我们似乎可以直接自己手写一个日志的实现类,来控制所有日志输出的格式。而SQL语句正是属于日志中的一种。那么我们就可以通过控制全部日志的格式从而控制SQL格式!

同时原文作者还给出了实现类的模板,可谓是将分享精神进行到底了。在此特别鸣谢原文作者!

通过仔细观察该实现类,不难发现,读取类名的方法正是该类的构造方法。这个方法中的参数clazz正是我们需要的类名信息!

那么我尝试在其他方法上也加入 String clazz 这个参数,试图找到其他方法的重载方法。然而并没有找到对应的重载方法。那么还有其他能够让其他方法读取到构造方法中的参数的办法呢,我灵光一闪,还~真~有~

于是我对源码进行了如下改造:

通过新建一个全局变量,将每次构造方法接收到的类名存储在该全局变量中,然后在其他方法中直接调用该全局变量即可!

改动完毕后,保存,运行。(记得修改yaml中的配置)

大功告成!所有日志都输出了该日志来源的类名,当然也就包括我们的SQL语句!

插件读取也正常,终于可以愉快的右键跳转到xml文件了!

源码分享

重写日志实现类

/**
 * @author BoJack
 * @version 1.0
 * @description 重写日志工厂的实现类,自定义输出日志格式
 * @date 2024/7/19 11:11:55
 */
public class PrintSqlLogImpl implements Log {

    /**
     * 全局变量,用于储存即将打印的SQL语句所属的实现类的引用名称
     */
    String className ="";

    public PrintSqlLogImpl(String clazz) {
        //每次打印前将当前SQL语句所属的实现类的引用名称给赋全局变量
        className = clazz;
        System.out.println("className:" + clazz);
    }

    @Override
    public void error(String s) {
 		//substring的作用是去除掉SQL前的'==>'
        System.out.println(className + s.substring(3));
    }

    @Override
    public void debug(String s) {
        System.out.println(className + s.substring(3));
    }

    @Override
    public void trace(String s) {
        System.out.println(className + s.substring(3));
    }

    @Override
    public void warn(String s) {
        System.out.println(className + s.substring(3));
    }

    @Override
    public boolean isDebugEnabled() {
        return true;
    }

    @Override
    public boolean isTraceEnabled() {
        return true;
    }

    @Override
    public void error(String s, Throwable e) {
        System.err.println(s);
        e.printStackTrace(System.err);
    }
}

配置文件

mybatis-plus:
  configuration:
	#改为你自己的实现类的引用名
    log-impl: com.XXXX.PrintSqlLogImpl 

总结

使用SqlSession来输出SQL语句时,仅通过更改logback的配置文件并不能影响SqlSession内部的输出格式,但可以通过重写mybatis的日志框架实现类来做到在所有日志前加入当前类名,而因为SQL也属于日志的一种,所以我们就可以通过控制全部日志的格式从而控制SQL格式。

特别鸣谢MybatisCodeHelper插件作者,以及所有互联网上分享自己经验的博主。

“让Java再次伟大!”(doge)

参考资料:

mybatis-plus配置控制台打印完整带参数SQL语句

mybatis:Creating a new SqlSession Closing non transactional SqlSession

Mybatis执行SQL的两种方式:Sqlsession与Mapper接口

【笔记】mybatis的sqlSession和Mapper详解