最新版本jdbcutils集成log4j做详细sql日志、自动释放连接...等

maven坐标
<!-- MySQL 8 --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.0.33</version></dependency><!-- Druid连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.16</version></dependency><!-- Jackson用于JSON处理 --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.15.2</version></dependency><!-- Log4j2日志 --><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.20.0</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.20.0</version></dependency>
import com.alibaba.druid.pool.DruidDataSource;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;import java.sql.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class JDBCUtils {private static DruidDataSource dataSource = null;private static ThreadLocal<ConnectionWrapper> connectionThreadLocal = new ThreadLocal<>();private static final Logger logger = LogManager.getLogger(JDBCUtils.class);private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);private static final ObjectMapper objectMapper = new ObjectMapper();private static final Pattern paramPattern = Pattern.compile("\\?");private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");private static void initializeDataSource() {if (dataSource == null) {synchronized (JDBCUtils.class) {if (dataSource == null) {dataSource = new DruidDataSource();dataSource.setUrl("jdbc:mysql://localhost:3306/hello-travel");dataSource.setUsername("root");dataSource.setPassword("root");dataSource.setInitialSize(5);dataSource.setMinIdle(5);dataSource.setMaxActive(20);try {dataSource.setFilters("stat");logger.info("Database connection pool initialized at {}",LocalDateTime.now().format(dateFormatter));} catch (SQLException e) {logger.error("Initialize Druid connection pool failed at {}",LocalDateTime.now().format(dateFormatter), e);throw new RuntimeException(e);}}}}}public static Connection getConnection() {ConnectionWrapper wrapper = connectionThreadLocal.get();if (wrapper == null || wrapper.isExpired()) {if (dataSource == null) {initializeDataSource();}try {Connection conn = dataSource.getConnection();wrapper = new ConnectionWrapper(conn);connectionThreadLocal.set(wrapper);scheduleConnectionRelease(wrapper);logger.debug("New database connection created at {}",LocalDateTime.now().format(dateFormatter));} catch (SQLException e) {logger.error("Get database connection failed at {}",LocalDateTime.now().format(dateFormatter), e);throw new RuntimeException("Get database connection failed", e);}}wrapper.renew();return wrapper.getConnection();}private static void scheduleConnectionRelease(ConnectionWrapper wrapper) {scheduler.schedule(() -> {if (wrapper.isExpired()) {closeConnection();logger.debug("Expired connection closed at {}",LocalDateTime.now().format(dateFormatter));}}, 5, TimeUnit.SECONDS);}public static void closeConnection() {ConnectionWrapper wrapper = connectionThreadLocal.get();if (wrapper != null) {try {wrapper.getConnection().close();logger.debug("Database connection closed at {}",LocalDateTime.now().format(dateFormatter));} catch (SQLException e) {logger.error("Close database connection failed at {}",LocalDateTime.now().format(dateFormatter), e);} finally {connectionThreadLocal.remove();}}}public static Object execute(String sql, Object... params) throws SQLException {// Record start timeLocalDateTime startTime = LocalDateTime.now();String formattedStartTime = startTime.format(dateFormatter);Connection connection = getConnection();ObjectNode logEntry = objectMapper.createObjectNode();logEntry.put("prePareStatementSql", sql);logEntry.put("startTime", formattedStartTime);// Create parameter mappingMap<String, Object> paramMap = createParamMap(sql, params);logEntry.set("params", objectMapper.valueToTree(paramMap));try (PreparedStatement stmt = connection.prepareStatement(sql)) {// Set parametersfor (int i = 0; i < params.length; i++) {stmt.setObject(i + 1, params[i]);}boolean isQuery = sql.trim().toLowerCase().startsWith("select");String actualSql = replaceSqlParams(sql, params);if (isQuery) {try (ResultSet rs = stmt.executeQuery()) {ArrayNode results = objectMapper.createArrayNode();ResultSetMetaData metaData = rs.getMetaData();int columnCount = metaData.getColumnCount();while (rs.next()) {ObjectNode row = objectMapper.createObjectNode();for (int i = 1; i <= columnCount; i++) {String columnName = metaData.getColumnLabel(i);Object value = rs.getObject(i);if (value != null) {row.putPOJO(columnName, value);} else {row.putNull(columnName);}}results.add(row);}// Log execution infoLocalDateTime endTime = LocalDateTime.now();String formattedEndTime = endTime.format(dateFormatter);long executionTimeMs = java.time.Duration.between(startTime, endTime).toMillis();logEntry.put("type", "query");logEntry.put("resultCount", results.size());logEntry.put("actualSql", actualSql);logEntry.put("endTime", formattedEndTime);logEntry.put("executionTimeMs", executionTimeMs);logger.info("SQL Execution Log: {}", logEntry.toString());return results;}} else {int affected = stmt.executeUpdate();// Log execution infoLocalDateTime endTime = LocalDateTime.now();String formattedEndTime = endTime.format(dateFormatter);long executionTimeMs = java.time.Duration.between(startTime, endTime).toMillis();ObjectNode result = objectMapper.createObjectNode();result.put("affectedRows", affected);logEntry.put("type", "update");logEntry.put("affectedRows", affected);logEntry.put("actualSql", actualSql);logEntry.put("endTime", formattedEndTime);logEntry.put("executionTimeMs", executionTimeMs);logger.info("SQL Execution Log: {}", logEntry.toString());return result;}} catch (SQLException e) {// Log error infoLocalDateTime endTime = LocalDateTime.now();String formattedEndTime = endTime.format(dateFormatter);long executionTimeMs = java.time.Duration.between(startTime, endTime).toMillis();logEntry.put("error", e.getMessage());logEntry.put("endTime", formattedEndTime);logEntry.put("executionTimeMs", executionTimeMs);logger.error("SQL Execution Log: {}", logEntry.toString());throw e;}
}private static Map<String, Object> createParamMap(String sql, Object[] params) {Map<String, Object> paramMap = new LinkedHashMap<>();Matcher matcher = paramPattern.matcher(sql);int paramIndex = 0;while (matcher.find() && paramIndex < params.length) {// 获取?前后的上下文int start = Math.max(0, matcher.start() - 20);int end = Math.min(sql.length(), matcher.end() + 20);String context = sql.substring(start, end);// 尝试提取参数名String paramName = extractParamName(context);String key = paramName != null ?String.format("%s (param%d)", paramName, paramIndex + 1) :String.format("param%d", paramIndex + 1);Object value = params[paramIndex];paramMap.put(key, value != null ? value : "null");paramIndex++;}return paramMap;}private static String extractParamName(String context) {Pattern pattern = Pattern.compile("(WHERE|AND|OR|SET|INSERT|UPDATE|DELETE)\\s+([\\w_]+)\\s*=\\s*\\?",Pattern.CASE_INSENSITIVE);Matcher matcher = pattern.matcher(context);if (matcher.find()) {return matcher.group(2);}return null;}private static String replaceSqlParams(String sql, Object[] params) {StringBuilder result = new StringBuilder(sql);int offset = 0;for (int i = 0; i < params.length; i++) {int questionMarkIndex = result.indexOf("?", offset);if (questionMarkIndex == -1) break;String paramValue = params[i] == null ? "NULL" :params[i] instanceof String || params[i] instanceof Date ?"'" + params[i] + "'" : params[i].toString();result.replace(questionMarkIndex, questionMarkIndex + 1, paramValue);offset = questionMarkIndex + paramValue.length();}return result.toString();}public static void shutdown() {logger.info("Initiating JDBCUtils shutdown at {}",LocalDateTime.now().format(dateFormatter));scheduler.shutdownNow();try {if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {logger.warn("Scheduler did not terminate within 5 seconds at {}",LocalDateTime.now().format(dateFormatter));}} catch (InterruptedException e) {Thread.currentThread().interrupt();logger.error("Shutdown interrupted at {}",LocalDateTime.now().format(dateFormatter), e);}if (dataSource != null) {dataSource.close();logger.info("Database connection pool closed at {}",LocalDateTime.now().format(dateFormatter));}}private static class ConnectionWrapper {private final Connection connection;private long lastAccessTime;private static final long TIMEOUT = 5000; // 5 seconds timeoutpublic ConnectionWrapper(Connection connection) {this.connection = connection;this.lastAccessTime = System.currentTimeMillis();}public Connection getConnection() {return connection;}public void renew() {this.lastAccessTime = System.currentTimeMillis();}public boolean isExpired() {return System.currentTimeMillis() - lastAccessTime > TIMEOUT;}}
}
resource目录的log4j2.xml文件内容
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"><Properties><!-- 定义日志格式,添加颜色支持 --><Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %highlight{%-5level}{ERROR=red, WARN=yellow, INFO=green} %logger{36} - %msg%n</Property></Properties><Appenders><!-- 控制台输出 --><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="${LOG_PATTERN}"/></Console></Appenders><Loggers><!-- 根日志记录器配置 --><Root level="INFO"><AppenderRef ref="Console"/></Root></Loggers>
</Configuration>


相关文章:
最新版本jdbcutils集成log4j做详细sql日志、自动释放连接...等
maven坐标 <!-- MySQL 8 --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.0.33</version></dependency><!-- Druid连接池 --><dependency><groupId&…...
jQuery快速填充非form数据
jQuery快速填充非form数据 先看看jQuery根据name填充form表单数据 <!DOCTYPE html> <html><head><script src"https://code.jquery.com/jquery-3.6.0.min.js"></script> </head><body><form id"myForm">…...
语音语言模型最新综述! 关于GPT-4o背后技术的尝试
近期,大型语言模型(LLMs)在生成文本和执行各种自然语言处理任务方面展现出了卓越的能力,成为了强大的AI驱动语言理解和生成的基础模型。然而,仅依赖于基于文本模态的模型存在显著局限性。这促使了基于语音的生成模型的发展,使其能够更自然、直观地与人类互动。 为了…...
根据用户选择的行和列数据构造数据结构(跨行跨列)
方案一 这段代码的功能是根据用户选择的行和列数据,生成一个适合复制粘贴的字符串表格。代码会先按列的 id 从小到大排序,再根据行列的选择关系将数据按顺序填入表格,每行之间使用换行符分隔,每列之间使用制表符分隔。如果某一行…...
Spark教程5-基本结构化操作
加载csv文件 df spark.read.format("json").load("/data/flight-data/json/2015-summary.json")Schema 输出Schema df.printSchema()使用Schema读取csv文件,以指定数据类型 from pyspark.sql.types import StructField, StructType, Strin…...
内置数据类型、变量名、字符串、数字及其运算、数字的处理、类型转换
内置数据类型 python中的内置数据类型包括:整数、浮点数、布尔类型(以大写字母开头)、字符串 变量名 命名变量要见名知意,确保变量名称具有描述性和意义,这样可以使得代码更容易维护,使用_可以使得变量名…...
Win/Mac/Android/iOS怎麼刪除代理設置?
設置代理設置的主要構成 IP 地址和端口 這些是代理伺服器配置的最基本組件。代理伺服器的IP地址引導互聯網流量,而端口號指定伺服器上的通信通道。 為什麼要刪除代理設置? 刪除代理設置通常是為了解決網路問題、提高速度、恢復安全性或過渡到新的網路…...
数据结构------手撕顺序表
文章目录 线性表顺序表的使用及其内部方法ArrayList 的扩容机制顺序表的几种遍历方式顺序表的优缺点顺序表的模拟实现洗牌算法 线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,…...
UDP(用户数据报协议)端口监控
随着网络的扩展,确保高效的设备通信对于优化网络功能变得越来越重要。在这个过程中,端口发挥着重要作用,它是实现外部设备集成的物理连接器。通过实现数据的无缝传输和交互,端口为网络基础设施的顺畅运行提供了保障。端口使数据通…...
【Java小白图文教程】-05-数组和排序算法详解
精品专题: 01.《C语言从不挂科到高绩点》课程详细笔记 https://blog.csdn.net/yueyehuguang/category_12753294.html?spm1001.2014.3001.5482 02. 《SpringBoot详细教程》课程详细笔记 https://blog.csdn.net/yueyehuguang/category_12789841.html?spm1001.20…...
OpenCV视觉分析之目标跟踪(1)计算密集光流的类DISOpticalFlow的介绍
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 这个类实现了 Dense Inverse Search (DIS) 光流算法。更多关于该算法的细节可以在文献 146中找到。该实现包含了三个预设参数集,以提…...
Lucas带你手撕机器学习——套索回归
好的,下面我将详细介绍套索回归的背景、理论基础、实现细节以及在实践中的应用,同时还会讨论其优缺点和一些常见问题。 套索回归(Lasso Regression) 1. 背景与动机 在机器学习和统计学中,模型的复杂性通常会影响其在…...
面试中的一个基本问题:如何在数据库中存储密码?
面试中的一个基本问题:如何在数据库中存储密码? 在安全面试中,“如何在数据库中存储密码?”是一个基础问题,但反映了应聘者对安全最佳实践的理解。以下是安全存储密码的最佳实践概述。 了解风险 存储密码必须安全&am…...
XML HTTP Request
XML HTTP Request 简介 XMLHttpRequest(XHR)是一个JavaScript对象,它最初由微软设计,并在IE5中引入,用于在后台与服务器交换数据。它允许网页在不重新加载整个页面的情况下更新部分内容,这使得网页能够实现动态更新,大大提高了用户体验。虽然名字中包含“XML”,但XML…...
TLS协议基本原理与Wireshark分析
01背 景 随着车联网的迅猛发展,汽车已经不再是传统的机械交通工具,而是智能化、互联化的移动终端。然而,随之而来的是对车辆通信安全的日益严峻的威胁。在车联网生态系统中,车辆通过无线网络与其他车辆、基础设施以及云端服务进行…...
当遇到 502 错误(Bad Gateway)怎么办
很多安装雷池社区版的时候,配置完成,访问的时候可能会遇到当前问题,如何解决呢? 客户端,浏览器排查 1.刷新页面和清除缓存 首先尝试刷新页面,因为有时候 502 错误可能是由于网络临时波动导致服务器无法连…...
学习记录:js算法(七十五): 加油站
文章目录 加油站思路一思路二思路三思路四思路五 加油站 在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发…...
强心剂!EEMD-MPE-KPCA-LSTM、EEMD-MPE-LSTM、EEMD-PE-LSTM故障识别、诊断
强心剂!EEMD-MPE-KPCA-LSTM、EEMD-MPE-LSTM、EEMD-PE-LSTM故障识别、诊断 目录 强心剂!EEMD-MPE-KPCA-LSTM、EEMD-MPE-LSTM、EEMD-PE-LSTM故障识别、诊断效果一览基本介绍程序设计参考资料 效果一览 基本介绍 EEMD-MPE-KPCA-LSTM(集合经验模态分解-多尺…...
yarn的安装与使用以及与npm的区别(安装过程中可能会遇到的问题)
一、yarn的安装 使用npm就可以进行安装 但是需要注意的一点是yarn的使用和node版本是有关系的必须是16.0以上的版本。 输入以下代码就可以实现yarn的安装 npm install -g yarn 再通过版本号的检查来确定,yarn是否安装成功 yarn -v二、遇到的问题 1、问题描述…...
大数据行业预测
大数据行业预测 编译 李升伟 和所有预测一样,我们必须谨慎对待这些预测,因为其中一些预测可能成不了事实。当然,真正改变游戏规则的创新往往出乎意料,甚至让最警惕的预言家也措手不及。所以,如果在来年发生了一些惊天…...
避开这些坑!海康威视嵌入式HR面常见‘送命题’与应答策略(附真实案例)
海康威视嵌入式HR面试避坑指南:6类高频"送命题"拆解与实战话术 在技术岗位的招聘流程中,HR面试往往是最容易被轻视却暗藏最多陷阱的环节。许多嵌入式开发者在技术面表现出色,却在看似轻松的HR面中意外折戟。通过对海康威视近三年嵌…...
EcomGPT-7B多语言能力:俄语商品→自动适配Wildberries平台标题规则
EcomGPT-7B多语言能力:俄语商品→自动适配Wildberries平台标题规则 1. 引言:跨境电商的本地化难题 如果你正在做俄罗斯电商,或者想把商品卖到Wildberries平台,一定遇到过这个头疼的问题:怎么把中文的商品信息&#x…...
别再手动打字了!用uniapp+百度语音识别,5分钟搞定语音转文字功能(附完整代码)
用UniApp百度语音识别实现高效语音转文字功能 在移动应用开发中,语音输入正逐渐成为提升用户体验的关键功能。想象一下,用户无需费力敲击虚拟键盘,只需轻按按钮说话,文字就能自动出现在输入框中——这种交互方式不仅自然流畅&…...
多无人机协同打击任务分配方法
随着无人机技术的不断成熟和完善,其军事应用的优势日益显现,近年来其在军事冲突中 所发挥的作用更使人们认识到,无人机在未来战争中将成为重要的军事装备。随着无人机在军 事中的大量应用,无人机集群协同执行任务将成为典型的应用…...
MedGemma-X实战教程:用status_gradio.sh实时监控GPU利用率与内存泄漏
MedGemma-X实战教程:用status_gradio.sh实时监控GPU利用率与内存泄漏 1. 为什么你需要实时监控MedGemma-X的GPU状态 MedGemma-X不是一台“开箱即用就永远稳定”的黑盒子。它是一套在GPU上高速运转的多模态影像认知系统——当它正在分析一张胸部X光片、生成结构化报…...
DeOldify处理超分辨率图像实战:应对大尺寸老照片的内存与计算挑战
DeOldify处理超分辨率图像实战:应对大尺寸老照片的内存与计算挑战 老照片修复,听起来是个挺有情怀的事儿。但当你真的拿到一张祖辈传下来的、扫描出来的超大尺寸老照片时,情怀可能瞬间就被现实浇灭了。动辄几千乘几千像素的扫描件࿰…...
跨平台音频格式兼容性处理:让FRCRN支持更多音视频文件
跨平台音频格式兼容性处理:让FRCRN支持更多音视频文件 你有没有遇到过这种情况?精心搭建了一个基于FRCRN模型的音频降噪服务,信心满满地准备上线,结果用户一上传文件,服务就报错。有的用户传的是手机录的.m4a…...
FastAPI安全防线:OAuth2 + JWT 实现无状态认证的完整流程
更多内容请见: 《Python Web项目集锦》 - 专栏介绍和目录 在现代Web应用开发中,安全认证是构建可靠API的基石。FastAPI通过其强大的安全组件,为开发者提供了实现安全、可扩展认证系统的工具。本文将深入剖析OAuth2与JWT在FastAPI中的整合实现,揭示无状态认证的完整流程,提…...
基于StructBERT的短视频评论情感分析系统搭建
基于StructBERT的短视频评论情感分析系统搭建 1. 引言 短视频平台每天产生海量用户评论,这些评论蕴含着用户对内容的真实感受和反馈。传统的人工审核方式效率低下,难以应对实时海量的评论数据。而基于StructBERT的情感分析系统能够自动识别评论的情感倾…...
半方差函数四大参数保姆级解读:从块金值到变程的空间自相关分析
半方差函数四大参数保姆级解读:从块金值到变程的空间自相关分析 刚接触地理统计时,看到"半方差函数"这个术语总让人望而生畏。但当我第一次用气象站数据绘制出那条神奇的曲线时,突然理解了空间数据背后隐藏的对话——就像侦探通过蛛…...
