Druid LogFilter输出可执行的SQL

配置

测试代码:

DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("xxx");
dataSource.setUsername("xxx");
dataSource.setPassword("xxx");
dataSource.setFilters("slf4j");
dataSource.setValidationQuery("SELECT 1");
dataSource.setTestOnBorrow(true);
dataSource.setTestWhileIdle(true);Connection connection = dataSource.getConnection();PreparedStatement stmt = connection.prepareStatement("select * from tb_order where id=?");
stmt.setInt(1,1);
stmt.execute();

在配置输出可执行的SQL之前,看下Druid的日志输出:

SQL占位符和参数分开打印

配置输出可执行的SQL

Java启动参数配置方式

-Ddruid.log.stmt.executableSql=true

logFilter参数直接配置:

<bean id="log-filter" class="com.alibaba.druid.filter.logging.Log4jFilter"><property name="statementExecutableSqlLogEnable" value="true" /></bean>

在配置输出可执行的SQL之后,看下Druid的日志输出:

打印出了可执行的SQL

源码

调用栈是DruidPooledPreparedStatement.execute->StatementProxyImpl.execute-> FilterChainImpl.preparedStatement_execute->FilterEventAdapter.preparedStatement_execute(所有Filter都继承了FilterEventAdapter)-> LogFilter.statementExecuteAfter->LogFilter.logExecutableSql。源码如下:

// DruidPooledPreparedStatement.execute
public boolean execute() throws SQLException {checkOpen();incrementExecuteCount();transactionRecord(sql);oracleSetRowPrefetch();conn.beforeExecute();try {//这个stmt是一个StatementProxyImpl实例return stmt.execute();} catch (Throwable t) {errorCheck(t);throw checkException(t);} finally {conn.afterExecute();}
}

StatementProxyImpl.execute:

// StatementProxyImpl.execute
public boolean execute() throws SQLException {updateCount = null;lastExecuteSql = sql;lastExecuteType = StatementExecuteType.Execute;lastExecuteStartNano = -1L;lastExecuteTimeNano = -1L;//调用过滤器链的preparedStatement_executefirstResultSet = createChain().preparedStatement_execute(this);return firstResultSet;
}

FilterChainImpl.preparedStatement_execute:

public boolean preparedStatement_execute(PreparedStatementProxy statement) throws SQLException {if (this.pos < filterSize) {// 调用过滤器的preparedStatement_execute方法return nextFilter().preparedStatement_execute(this, statement);}return statement.getRawObject().execute();
}

所有过滤器都继承了FilterEventAdapter,看名字是个和事件有关的类,这个父类里面实现了preparedStatement_execute方法:

// FilterEventAdapter.preparedStatement_execute
public boolean preparedStatement_execute(FilterChain chain, PreparedStatementProxy statement) throws SQLException {try {statementExecuteBefore(statement, statement.getSql());// 递归调用配置的所有过滤器的preparedStatement_execute方法,直到真正执行完statementboolean firstResult = chain.preparedStatement_execute(statement);// 在执行完statement后执行,这是个空方法,具体的逻辑在子类里面实现,即LogFilterthis.statementExecuteAfter(statement, statement.getSql(), firstResult);return firstResult;} catch (SQLException error) {statement_executeErrorAfter(statement, statement.getSql(), error);throw error;} catch (RuntimeException error) {statement_executeErrorAfter(statement, statement.getSql(), error);throw error;} catch (Error error) {statement_executeErrorAfter(statement, statement.getSql(), error);throw error;}}

LogFilter.statementExecuteAfter方法:

protected void statementExecuteAfter(StatementProxy statement, String sql, boolean firstResult) {// 打印可执行的SQLlogExecutableSql(statement, sql);// 统计SQL执行的时间if (statementExecuteAfterLogEnable && isStatementLogEnabled()) {statement.setLastExecuteTimeNano();double nanos = statement.getLastExecuteTimeNano();double millis = nanos / (1000 * 1000);statementLog("{conn-" + statement.getConnectionProxy().getId() + ", " + stmtId(statement) + "} executed. "+ millis + " millis. " + sql);}
}

LogFilter.logExecutableSql:

private void logExecutableSql(StatementProxy statement, String sql) {// 没有配置输出可执行SQL,直接返回if ((!isStatementExecutableSqlLogEnable()) || !isStatementLogEnabled()) {return;}// 获取SQL参数数量int parametersSize = statement.getParametersSize();if (parametersSize == 0) {statementLog("{conn-" + statement.getConnectionProxy().getId() + ", " + stmtId(statement) + "} executed. "+ sql);return;}// 获取SQL的参数,存到parameters中List<Object> parameters = new ArrayList<Object>(parametersSize);for (int i = 0; i < parametersSize; ++i) {JdbcParameter jdbcParam = statement.getParameter(i);parameters.add(jdbcParam != null? jdbcParam.getValue(): null);}DbType dbType = DbType.of(statement.getConnectionProxy().getDirectDataSource().getDbType());// 最终由SQLUtils根据参数列表和预执行SQL,转换为可执行SQLString formattedSql = SQLUtils.format(sql, dbType, parameters, this.statementSqlFormatOption);// statementLog是个抽象方法,由具体的日志过滤器类实现(如Slf4jLogFilter)statementLog("{conn-" + statement.getConnectionProxy().getId() + ", " + stmtId(statement) + "} executed. "+ formattedSql);
}

总结

输出可执行SQL在日常排查SQL执行错误还是很实用的。其原理是在PreparedStatement.execute执行之后,调用SQLUtils.format打印出可执行的SQL。FilterEventAdapter这个类很关键,它会在SQL执行之前或者之后,调用扩展的处理,具体的处理逻辑又委派给子类实现。

Springboot Druid配置可执行sql配置_druid 执行sql_孙陆泉的博客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/75100.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RTSP/Onvif安防视频云平台EasyNVR视频监控汇聚平台显示视频流却无法播放,是什么原因?

EasyNVR是基于RTSP/Onvif协议的视频平台&#xff0c;拥有视频监控直播、录像、云存储、检索与回看、国标级联等视频能力&#xff0c;可支持将接入的视频流进行全平台、全终端的分发&#xff0c;分发的视频流包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等格式。 有用户反馈…

LLMs之Baichuan 2:《Baichuan 2: Open Large-scale Language Models》翻译与解读

LLMs之Baichuan 2&#xff1a;《Baichuan 2: Open Large-scale Language Models》翻译与解读 导读&#xff1a;2023年9月6日&#xff0c;百川智能重磅发布Baichuan 2。科技论文主要介绍了Baichuan 2&#xff0c;一个开源的大规模语言模型&#xff0c;以及其在多个领域的性能表现…

基于云计算的区域LIS系统系统源码

在医疗机构内部&#xff0c;院内实验室主要负责本院临床科室的检验&#xff0c;院内LIS系统必须满足实验室日常的标本处理入库、仪器联机、检验结果处理、报告打印、报告发布、检验信息统计、检验信息报告发布、标本流程、外部医疗机构检验报告调阅等工作。 在医疗机构间&#…

快速排序详解

前言 快排是不稳定的排序&#xff0c;快排的适用场景是无序的序列&#xff0c;例如此时有一个数组是有序的 / 逆序的&#xff0c;此时的快排效率是最慢的。 过程&#xff1a; 找一个基准值&#xff0c;找的过程就以挖坑法的方式填坑&#xff0c;第一次排序以挖坑发填完坑之后&a…

【聚类】K-Means聚类

cluster&#xff1a;簇 原理&#xff1a; 这边暂时没有时间具体介绍kmeans聚类的原理。简单来说&#xff0c;就是首先初始化k个簇心&#xff1b;然后计算所有点到簇心的欧式距离&#xff0c;对一个点来说&#xff0c;距离最短就属于那个簇&#xff1b;然后更新不同簇的簇心&a…

[Linux]文件系统

[Linux]文件系统 文件系统是操作系统的一部分&#xff0c;负责组织、存储和管理存储在外部设备上的文件和目录&#xff0c;也就是操作系统管理外设中的文件的策略。本文讲解的是Ext2文件系统。Linux操作系统使用的就是Ext系列的文件系统。 文章目录 [Linux]文件系统了解磁盘结构…

如何选择合适的HTTP代理服务器

HTTP代理服务器是一种常见的网络代理方式&#xff0c;它可以帮助用户隐藏自己的IP地址&#xff0c;保护个人隐私和安全。然而&#xff0c;选择合适的HTTP代理服务器并不容易&#xff0c;需要考虑多个因素。本文将介绍如何选择合适的HTTP代理服务器。 了解代理服务器的类型 HTT…

Web Components详解-Shadow DOM插槽

前言 插槽实际上也属于组件通信的一种方式&#xff0c;但是由于其强大的api和实用性&#xff0c;我将其单独拆开来介绍。 定义 Slot&#xff08;插槽&#xff09;是Web Components中一个重要的特性&#xff0c;它允许在组件内部定义占位符&#xff0c;以便父组件可以向其中插…

【Java并发】聊聊ReentrantReadWriteLock锁降级和StampedLock邮戳锁

面试题 1.你说你用过读写锁&#xff0c;锁饥饿问题是什么&#xff1f; 2.有没有比读写锁更快的锁&#xff1f; 3.StampedLock知道吗?(邮戳锁/票据锁) 4.ReentrantReadWriteLock有锁降级机制策略你知道吗&#xff1f; 在并发编程领域&#xff0c;有多线程进行提升整体性能&…

流程图 and/or/xor 讲解

and表示后续2个活动同时触发&#xff0c; or表示后续2个活动可触发其中的1个或2个&#xff0c;无排他性&#xff0c;也就是每个活动的触发不影响其他活动&#xff1b; xor表示后续2个活动只触发一个&#xff0c;有排他性&#xff0c;也就是只能触发其中一个。 示例演示“OR”…

云原生Kubernetes:Yaml文件编写

目录 一、理论 1.Kubernetes与yaml文件 二、实验 1.Kubernetes与yaml文件 三、问题 1.kubectl create 和 kubectl apply区别 四、总结 一、理论 1.Kubernetes与yaml文件 &#xff08;1&#xff09;Kubernetes支持管理资源对象的文件格式 Kubernetes支持YAML 和JSON 格…

基于微信小程序的智能垃圾分类回收系统,附源码、教程

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 1 简介 视频演示地址&#xff1a; 基于微信小程序的智能垃圾分类回收系统&#xff0c;可作为毕业设计 小…

PDF文件太大怎么办?三招教会你PDF文件压缩

PDF文件太大怎么办&#xff1f;这是许多人在处理PDF文件时遇到的问题。为了帮助大家解决这个问题&#xff0c;下面总结了三个可以解决PDF文件过大问题的方法&#xff0c;需要的朋友抓紧来看看吧~ 方法一&#xff1a;使用嗨格式压缩大师 嗨格式压缩大师是一款功能强大的PDF压缩…

无涯教程-JavaScript - IMSECH函数

描述 IMSECH函数以x yi或x yj文本格式返回复数的双曲正割。复数的双曲正割被定义为双曲余弦的倒数,即 六(z) 1/cosh(z) 语法 IMSECH (inumber)争论 Argument描述Required/OptionalInumberA complex number for which you want the hyperbolic secant.Required Notes Ex…

手机usb连接电脑上网怎么做?掌握2个方法即可!

“我的电脑不知道怎么就连不上网络了&#xff0c;之前好像听说可以使用手机usb连接网络上网&#xff0c;但是不知道具体应该怎么操作。有没有知道详细操作步骤的朋友可以分享一下呀&#xff01;” 在需要临时共享手机网络连接或电脑无法连接Wi-Fi的情况下&#xff0c;将手机通过…

【C++基础】实现日期类

​&#x1f47b;内容专栏&#xff1a; C/C编程 &#x1f428;本文概括&#xff1a; C实现日期类。 &#x1f43c;本文作者&#xff1a; 阿四啊 &#x1f438;发布时间&#xff1a;2023.9.7 对于类的成员函数的声明和定义&#xff0c;我们在类和对象上讲到过&#xff0c;需要进行…

c++通过tensorRT调用模型进行推理

模型来源&#xff1a; 算法工程师训练得到的onnx模型 c对模型的转换&#xff1a; 拿到onnx模型后&#xff0c;通过tensorRT将onnx模型转换为对应的engine模型&#xff0c;注意&#xff1a;训练用的tensorRT版本和c调用的tensorRT版本必须一致。 如何转换&#xff1a; 算法工…

2020年12月 C/C++(二级)真题解析#中国电子学会#全国青少年软件编程等级考试

C/C++编程(1~8级)全部真题・点这里 第1题:数组指定部分逆序重放 将一个数组中的前k项按逆序重新存放。例如,将数组8,6,5,4,1前3项逆序重放得到5,6,8,4,1。 时间限制:1000 内存限制:65536 输入 输入为两行: 第一行两个整数,以空格分隔,分别为数组元素的个数n(1 < n…

在Qt5中SQLite3的使用

一、SQLite简要介绍 什么是SQLite SQLite是一个进程内的库&#xff0c;实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。它是一个零配置的数据库&#xff0c;这意味着与其他数据库不一样&#xff0c;您不需要在系统中配置。 就像其他数据库&#xff0c;S…

基于javaweb的CT图像管理系统(servlet+jsp)

系统简介 本项目采用eclipse工具开发&#xff0c;jspservletjquery技术编写&#xff0c;数据库采用的是mysql&#xff0c;navicat开发工具。 三个角色&#xff1a;管理员&#xff0c;普通用户&#xff0c;医生 模块简介 管理员&#xff1a; 1、登录 2、用户管理 3、医生管…