Mybatis使用SqlSession接口输出SQL日志强制显示类名
最终解决方案在文末,可直接下滑到底部获取源码。
前言
在使用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:Creating a new SqlSession Closing non transactional SqlSession