最新版本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.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/temp-gui");
                    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 time
        LocalDateTime 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 mapping
        Map<String, Object> paramMap = createParamMap(sql, params);
        logEntry.set("params", objectMapper.valueToTree(paramMap));

        PreparedStatement stmt = connection.prepareStatement(sql);
        // Set parameters
        for (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) {
            ResultSet rs = stmt.executeQuery();

            // Log execution info
            LocalDateTime endTime = LocalDateTime.now();
            String formattedEndTime = endTime.format(dateFormatter);
            long executionTimeMs = java.time.Duration.between(startTime, endTime).toMillis();

            logEntry.put("type", "query");
            logEntry.put("actualSql", actualSql);
            logEntry.put("endTime", formattedEndTime);
            logEntry.put("executionTimeMs", executionTimeMs);

            logger.info("SQL Execution Log: {}", logEntry.toString());
            return rs;
        } else {
            int affected = stmt.executeUpdate();

            // Log execution info
            LocalDateTime 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 affected;
        }
    }


    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 timeout

        public 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>

在这里插入图片描述
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

sass软件登录设定——未来之窗行业应用跨平台架构

一、saas软件开发中登录设计 以为大公司为参考思迅在登录时候需要录入商户号 二、独立商户商户好处 1.每个店铺的账户是独立的&#xff0c;保护商户职员账户信息的相对安全。 2.不同店铺可以试用相同用户名

qt QMediaPlaylist

QMediaPlaylist 是 Qt Multimedia 模块中的一个类&#xff0c;用于管理媒体文件的播放列表。它提供了一种方便的方式来组织和控制多媒体内容的播放&#xff0c;如音频和视频文件。 主要方法 QMediaPlaylist(00bject *parent nullptr):构造一个新的媒体播放列表对象。void add…

低代码平台如何通过AI赋能,实现更智能的业务自动化?

引言 随着数字化转型的加速推进&#xff0c;企业在日常运营中面临的业务复杂性与日俱增。如何快速响应市场需求&#xff0c;优化流程&#xff0c;并降低开发成本&#xff0c;成为各行业共同关注的核心问题。低代码平台作为一种能够快速构建应用程序的工具&#xff0c;因其可视化…

ArcGIS003:ArcMap常用操作0-50例动图演示

摘要&#xff1a;本文以动图形式介绍了ArcMap软件的基本操作&#xff0c;包括快捷方式创建、管理许可服务、操作界面元素&#xff08;如内容列表、目录树、搜索窗口、工具箱、Python窗口、模型构建器窗口等&#xff09;的打开与关闭、位置调整及合并&#xff0c;设置默认工作目…

【JavaEE初阶】网络原理-TCP连接管理之“三次握手和四次挥手”

前言 &#x1f31f;&#x1f31f;本期讲解关于TCP协议的重要的机制“连接的建立和断开”~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &…

Linux 部署 mysql

Linux&#xff08;四&#xff09;- Ubuntu安装Mysql_mysql-community-server-core depends on libaio1 (>-CSDN博客 #创建用户 打开终端。 登录到MySQL服务器&#xff1a; mysql -u root -p 创建新用户。替换new_user为您想要的用户名&#xff0c;password为新用户的密码&am…

python异常监测-ARIMA(自回归积分滑动平均模型)

系列文章目录 Python异常检测- Isolation Forest&#xff08;孤立森林&#xff09; python异常检测 - 随机离群选择Stochastic Outlier Selection (SOS) python异常检测-局部异常因子&#xff08;LOF&#xff09;算法 Python异常检测- DBSCAN Python异常检测- 单类支持向量机(…

python之数据结构与算法(数据结构篇)-- 元组

一、元组的概念 其实&#xff0c;所谓的“元组”就是一组不能改变的特殊数组(一般情况下)&#xff0c;这里我们去创建一个“小羊元组”&#xff0c;大家就能更加明白其中的意思了。 二、元组实现思路 1.我们去定义一个“羊村小羊”的元组 # 创建一个"羊村小羊"的元…

SystemC学习(2)— APB_SRAM的建模与测试

SystemC学习&#xff08;2&#xff09;— APB_SRAM的建模与测试 一、前言 二、APB_SRAM建模 编写APB_SRAM模型文件apb_sram.h文件如下所示&#xff1a; #ifndef __APB_SRAM_H #define __APB_SRAM_H#include "systemc.h"const int ADDR_SIZE 32; const int DATA_…

【专题】计算机网络之数据链路层

数据链路层的地位&#xff1a;网络中的主机、路由器等都必须实现数据链路层。 数据链路层信道类型&#xff1a; 点对点信道。 使用一对一的点对点通信方式。 广播信道。 使用一对多的广播通信方式。 必须使用专用的共享信道协议来协调这些主机的数据发送。 1. 使用点对点信道…

三次握手与四次挥手

1.三次握手 1.1状态机 客户端 SYN_SENT&#xff1a;客户端发送SYN报文段请求建立连接 ESTABLISHED&#xff1a;在收到服务端发来的SYN请求报文以及确认报文后就建立客户端到服务端的连接 服务端 LISTEN&#xff1a;一开始时LISTEN状态&#xff0c;等待客户端的SYN请求 SY…

群晖系统基本命令

切换超级管理员 sudo -i 查询系统 运行的所有服务 synoservicecfg --list 启动服务命令(该命令需要使用超级管理员) #老版本群晖使用synoservice命令 synoservice --start 服务名称#新版本群晖使用systemctl命令 systemctl start 服务名称 synoservice所管理的服务配置文…

MySql数据库中数据类型

本篇将介绍在 MySql 中的所有数据类型&#xff0c;其中主要分为四类&#xff1a;数值类型、文本和二进制类型、时间日期、String 类型。如下&#xff08;图片来源&#xff1a;MySQL数据库&#xff09;&#xff1a; 目录如下&#xff1a; 目录 数值类型 1. 整数类型 2. …

【网站项目】SpringBoot397学校财务管理系统

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…

【论文阅读】SRGAN

学习资料 论文题目&#xff1a;基于生成对抗网络的照片级单幅图像超分辨率&#xff08;Photo-Realistic Single Image Super-Resolution Using a Generative Adversarial Network&#xff09;论文地址&#xff1a;https://arxiv.org/abs/1609.04802 代码&#xff1a;GitHub - x…

【Python爬虫实战】Selenium自动化网页操作入门指南

#1024程序员节&#xff5c;征文# &#x1f308;个人主页&#xff1a;易辰君-CSDN博客 &#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/2401_86688088/category_12797772.html ​ 目录 前言 一、准备工作 &#xff08;一&#xff09;安装 Selenium 库 &#xff0…

SpringBoot项目里怎么简单高效使用Redis?我选择使用Lock4j

文章目录 前言正文1、Lock4j的代码仓库2、pine-manage-common-redis的项目结构3、pine-manage-common-redis 的完整代码3.1 maven依赖&#xff1a;pom.xml3.2 redis连接参数&#xff1a;application.yaml3.3 RedisCache.java3.4 CacheConfig.java3.5 RedissonClientUtil.java3.…

Python | Leetcode Python题解之第509题斐波那契数

题目&#xff1a; 题解&#xff1a; class Solution:def fib(self, n: int) -> int:if n < 2:return nq [[1, 1], [1, 0]]res self.matrix_pow(q, n - 1)return res[0][0]def matrix_pow(self, a: List[List[int]], n: int) -> List[List[int]]:ret [[1, 0], [0, …

Redisson(三)应用场景及demo

一、基本的存储与查询 分布式环境下&#xff0c;为了方便多个进程之间的数据共享&#xff0c;可以使用RedissonClient的分布式集合类型&#xff0c;如List、Set、SortedSet等。 1、demo <parent><groupId>org.springframework.boot</groupId><artifact…

spygalss cdc 检测的bug(二)

当allow_qualifier_merge设置为strict的时候&#xff0c;sg是要检查门的极性的。 如果qualifier和src经过与门汇聚&#xff0c;在同另一个src1信号或门汇聚&#xff0c;sg是报unsync的。 假设当qualifier为0时&#xff0c;0&&src||src1src1&#xff0c;src1无法被gat…