百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

Mybatis 调试输出SQL语句,到底是如何实现的呢?

ztj100 2024-11-18 19:20 21 浏览 0 评论

Java 开发中常用的几款日志框架有很多种,并且这些日志框架来源于不同的开源组织,给用户暴露的接口也有很多不同之处,所以很多开源框架会自己定义一套统一的日志接口,兼容上述第三方日志框架,供上层使用。

一般实现的方式是使用适配器模式,将各个第三方日志框架接口转换为框架内部自定义的日志接口。MyBatis 也提供了类似的实现,这里我们就来简单了解一下。

适配器模式是什么?

简单来说,适配器模式主要解决的是由于接口不能兼容而导致类无法使用的问题,这在处理遗留代码以及集成第三方框架的时候用得比较多。其核心原理是:通过组合的方式,将需要适配的类转换成使用者能够使用的接口。

日志模块

MyBatis 自定义的 Log 接口位于 org.apache.ibatis.logging 包中,相关的适配器也位于该包中。

首先是 LogFactory 工厂类,它负责创建 Log 对象,在 LogFactory 类中有一段静态代码块,其中会依次加载各个第三方日志框架的适配器。

static {
    tryImplementation(LogFactory::useSlf4jLogging);
    tryImplementation(LogFactory::useCommonsLogging);
    tryImplementation(LogFactory::useLog4J2Logging);
    tryImplementation(LogFactory::useLog4JLogging);
    tryImplementation(LogFactory::useJdkLogging);
    tryImplementation(LogFactory::useNoLogging);
}

JDK Logging 的加载流程(useJdkLogging() 方法)为例,其具体代码实现和注释如下:

/**
 * 首先会检测 logConstructor 字段是否为空,
 * 1.如果不为空,则表示已经成功确定当前使用的日志框架,直接返回;
 * 2.如果为空,则在当前线程中执行传入的 Runnable.run() 方法,尝试确定当前使用的日志框架
 */
private static void tryImplementation(Runnable runnable) {
    if (logConstructor == null) {
        try {
            runnable.run();
        } catch (Throwable t) {
            // ignore
        }
    }
}

public static synchronized void useJdkLogging() {
    setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class);
}

private static void setImplementation(Class<? extends Log> implClass) {
    try {
        // 获取适配器的构造方法
        Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
        // 尝试加载适配器,加载失败会抛出异常
        Log log = candidate.newInstance(LogFactory.class.getName());
        // 加载成功,则更新logConstructor字段,记录适配器的构造方法
        logConstructor = candidate;
    } catch (Throwable t) {
        throw new LogException("Error setting Log implementation.  Cause: " + t, t);
    }
}

打印SQL语句

如何开启打印

这里演示Mybatis在运行时怎么输出SQL语句,具体分析见原理章节。

单独使用Mybatis

mybatis.xml配置文件中添加如下配置:

<setting name="logImpl" value="STDOUT_LOGGING" />

和SpringBoot整合

有两种方式,第一种也是利用StdOutImpl实现类去实现打印,在application.yml配置文件填写如下:

#mybatis配置
mybatis:
  # 控制台打印sql日志
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

其次我们还可以通过指定日志级别来输出SQL语句:

SpringBoot默认使用的SL4J(日志门面)+Logback(具体实现)的日志组合

logging:
  level:
    xx包名: debug

简单分析原理

这里我们直接看到org.apache.ibatis.executor.BaseExecutor#getConnection方法,了解Mybatis的应该都知道Mybatis在执行sql操作的时候会去获取数据库连接

protected Connection getConnection(Log statementLog) throws SQLException {
    Connection connection = transaction.getConnection();
    // 判断日志级别是否为Debug,是的话返回代理对象
    if (statementLog.isDebugEnabled()) {
        return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
        return connection;
    }
}

可以看到我注释的那行,它通过判断日志级别来判断是否返回ConnectionLogger代理对象,那么我们前面提到 Log 接口的实现类中StdOutImpl它的isDebugEnabled其实是永远返回 true,代码如下:

并且它直接用的 System.println去输出的SQL信息

public class StdOutImpl implements Log {
    
  // ...省略无关代码
      
  @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);
  }

  @Override
  public void error(String s) {
    System.err.println(s);
  }
  // ...省略无关代码
}

到这里起码你知道了为什么我们通过配置 MyBatis 所用日志的具体实现 logImpl就可以实现日志输出到控制台的效果了。

那么我们还可以深究一下 statementLog 是在什么时候变成 StdOutImpl的,在解析Mybatis配置文件的时候,会去读取我们配置的logImpl属性,然后通过LogFactory.useCustomLogging方法先指定好适配器的构造方法

// org.apache.ibatis.builder.xml.XMLConfigBuilder#loadCustomLogImpl  
private void loadCustomLogImpl(Properties props) {
    Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
    configuration.setLogImpl(logImpl);
}

public void setLogImpl(Class<? extends Log> logImpl) {
    if (logImpl != null) {
        this.logImpl = logImpl;
        LogFactory.useCustomLogging(this.logImpl);
    }
}

然后在构建MappedStatement的时候就已经将日志对象初始化好了

每个MappedStatement对应了我们自定义Mapper接口中的一个方法,它保存了开发人员编写的SQL语句、参数结构、返回值结构、Mybatis对它的处理方式的配置等细节要素,是对一个SQL命令是什么、执行方式的完整定义。

public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
    // ...省略无关代码
    mappedStatement.statementLog = LogFactory.getLog(logId);
    mappedStatement.lang = configuration.getDefaultScriptingLanguageInstance();
}

public static Log getLog(String logger) {
    try {
        return logConstructor.newInstance(logger);
    } catch (Throwable t) {
        throw new LogException("Error creating logger for logger " + logger + ".  Cause: " + t, t);
    }
}

最后SpringBoot的就不概述了

  • 第一种方式其实也是同理
  • 第二种方式是通过修改了日志级别,然后使 isDebugEnabled 返回true,去返回代理对象,然后去输出SQL语句。

感兴趣的还可以看看SQL语句的输出是怎么输出的,具体在 ConnectionLogger的invoke方法中,你会发现熟悉的Preparing: "和"Parameters: "。

来源:blog.csdn.net/qq_39809458/article/details/121793630

相关推荐

干货 | 各大船公司VGM提交流程(msc船运公司提单查询)

VGM(VerifiedGrossMass)要来了,大外总管一本正经来给大家分享下各大船公司提交VGM流程。1,赫伯罗特(简称HPL)首先要注册账户第一,登录进入—选择product------...

如何修改图片详细信息?分享三个简单方法

如何修改图片详细信息?分享三个简单方法我们知道图片的详细信息里面包含了很多属性,有图片的创建时间,修改时间,地理位置,拍摄时间,还有图片的描述等信息。有时候为了一些特殊场景的需要我们需要对这些信息进行...

实用方法分享:没有图像处理软件,怎么将一张照片做成九宫格?

在发朋友圈时,如果把自己的照片做成九宫格,是不是更显得高大上?可能你问,是不是要借助图片处理软件,在这里,我肯定告诉你,不需要!!!你可能要问,那怎么实现呢?下面你看我是怎么做的,一句代码都不写,只是...

扫描档PDF也能变身“最强大脑”?RAG技术解锁尘封的知识宝藏!

尊敬的诸位!我是一名物联网工程师。关注我,持续分享最新物联网与AI资讯和开发实战。期望与您携手探寻物联网与AI的无尽可能。今天有网友问我扫描档的PDF文件能否做知识库,其实和普通pdf处理起来差异...

这两个Python库,轻而易举就能实现MP4与GIF格式互转,太好用了

mp4转gif的原理其实很简单,就是将mp4文件的帧读出来,然后合并成一张gif图。用cv2和PIL这两个库就可以轻松搞定。importglobimportcv2fromPILimpo...

python图片处理之图片切割(python把图片切割成固定大小的子图)

python图片切割在很多项目中都会用到,比如验证码的识别、目标检测、定点切割等,本文给大家带来python的两种切割方式:fromPILimportImage"""...

python+selenium+pytesseract识别图片验证码

一、selenium截取验证码#私信小编01即可获取大量Python学习资源#私信小编01即可获取大量Python学习资源#私信小编01即可获取大量Python学习资源importjso...

如何使用python裁剪图片?(python图片截取)

如何使用python裁剪图片如上图所示,这是一张包含了各类象棋棋子的图片。我们需要将其中每一个棋子都裁剪出来,此时可以利用python的...

Python rembg 库去除图片背景(python 删除图片)

rembg是一个强大的Python库,用于自动去除图片背景。它基于深度学习模型(如U^2-Net),能够高效地将前景物体从背景中分离,生成透明背景的PNG图像。本教程将带你从安装到实际应用...

「python脚本」批量修改图片尺寸&amp;视频安帧提取

【python脚本】批量修改图片尺寸#-*-coding:utf-8-*-"""CreatedonThuAug2316:06:352018@autho...

有趣的EXCEL&amp;vba作图(vba画图表)

还记不记得之前有个日本老爷爷用EXCEL绘图,美轮美奂,可谓是心思巧妙。我是没有那样的艺术细胞,不过咱有自己的方式,用代码作图通过vba代码将指定的图片写入excel工作表中,可不是插入图片哦解题思...

怎么做到的?用python制作九宫格图片,太棒了

1.应用场景当初的想法是:想把一张图切割成九等份,发布到微信朋友圈,切割出来的图片,上传到朋友圈,发现微信不按照我排列的序号来排版。这样的结果是很耗时间的。让我深思,能不能有一种,直接拼接成一张...

Python-连续图片合成视频(python多张图叠加为一张)

前言很多时候,我们需要将图片直接转成视频。下面介绍用python中的OpenCV将进行多张图合成视频。cv2安装不要直接用pipinstallcv2,这会报错。有很多人建议用打开window自带的...

如何把多个文件夹里的图片提取出来?文件夹整理合并工具

在项目管理中,团队成员可能会将项目相关的图片资料分散存储在不同的文件夹中,以便于分类和阶段性管理。然而,当项目进入汇报或总结阶段时,需要将所有相关图片整合到一个位置,以便于制作演示文稿、报告或进行项目...

超简单!为图片和 PDF 上去掉水印(pdf图片和水印是一体,怎么去除)

作者:某某白米饭...

取消回复欢迎 发表评论: