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

news2024/11/24 18:57:48

在这里插入图片描述

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 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));

    try (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) {
            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 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("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 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 result;
        }
    } catch (SQLException e) {
        // Log error info
        LocalDateTime 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 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>

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

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

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

相关文章

记一次AWS服务器扩容

1、首先通过下列命令列出设备详情&#xff0c;可以看到红色框起来的部分有160G&#xff0c;需要把新增的20G扩容到根目录(139.9)上 lsblk查看文件系统 df -h2.执行sudo growpart /dev/xvda 1即可把20G的空间扩容到根目录上 扩容成功 但是可以看到并未生效 3.列出文件系统格…

菜叶子芯酸笔记2:服务器、互联技术和AI芯片参数解读

服务器相关知识 服务器是一种高性能计算机&#xff0c;作为网络的节点&#xff0c;存储、处理网络上80%的数据、信息&#xff0c;因此也被称为网络的灵魂。 服务器的分类 种类 描述 塔式服务器(tower server) 正面似PC机&#xff0c;但侧面长度长很多&#xff0c;无统一标准…

pair类型应用举例

在main.cpp里输入程序如下&#xff1a; #include <iostream> //使能cin(),cout(); #include <utility> //使能pair数据类型; #include <string> //使能string字符串; #include <stdlib.h> //使能exit(); //pair类型可以将两个相同的或不同类…

2024年10月-2025年5月 Oracle 19c OCM 考试安排

2024年10月-2025年5月 Oracle 19c OCM 考试安排&#xff1a; 北京考场&#xff1a; 上海考场&#xff1a; 更新时间&#xff1a;2024年10月25日 Oracle 19c OCM往期学员成绩展示&#xff1a; Oracle 19c OCM认证证书&#xff08;电子版&#xff09;

数理统计(第3章第1节:假设检验的基本概念)

目录 假设检验&#xff1a;对母体的分布或者母体分布中的未知参数提出某种假设&#xff0c;由子样推断是否接受该种假设 假设检验的基本概念&#xff08;概率性质的反证法&#xff09; 假设检验&#xff1a;对母体的分布或者母体分布中的未知参数提出某种假设&#xff0c;由子…

云计算欲上九天,AI大模型能否推波助澜?

大数据产业创新服务媒体 ——聚焦数据 改变商业 时代洪流滚滚朝前&#xff0c;千禧年的“世界末日”并未如期而至&#xff0c;但信息大爆炸了&#xff0c;有人开始探索那80%常规生活以外的“20%世界”。谁都未曾想到恰恰这20%成为了如今赛博世界的“种子”&#xff0c;数据不再…

考研读研生存指南,注意事项

本视频&#xff0c;涉及考研读研的方方面面&#xff0c;从考研初试→复试面试→研究生生活→导师相处→论文专利写作混毕业&#xff0c;应有尽有。有了他&#xff0c;你的研究生生涯稳了。 读研考研注意事项&#xff0c;研究生生存指南。_哔哩哔哩_bilibili 一、考研初试注意事…

C# SM2 加签、验签工具

目录 效果 项目 代码 下载 效果 项目 代码 using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Signers; using Org.BouncyCastle.Asn1.GM; using System; using System.Text; using System.Windows.Forms; using Org.BouncyCastle.Asn1.X9; using…

二分查找_在排序数组中查找元素的第一个和最后一个位置

1.朴素二分查找 .二分查找 二分查找思路&#xff1a; 1.left0,rightnums.size()-1&#xff08;最后一个元素下标&#xff09;&#xff0c;取中间元素下标 midleft(right-left)/2 &#xff08;防溢出&#xff09; 2.nums[mid]>target &#xff0c;说明mid右边的元素都大于ta…

软考:缓存和数据库数据一致性问题

参考&#xff1a;CSDN博客&#xff0c;8种方案 前言 为什么要一致 如果数据不一致&#xff0c;那么业务应用从缓存中读取的数据就不是最新的数据&#xff0c;这会导致严重的错误 数据一致性是什么 缓存中有数据&#xff0c;那么&#xff0c;缓存的数据需要和数据库中的值相同 …

vue图片加载失败的图片

1.vue图片加载失败的图片 这个问题发生在测试环境和开发本地&#xff0c;线上环境是可以的&#xff0c;测试环境估计被第三方屏蔽了 2.图片有&#xff0c;却加载不出来 <template v-slot:imageUrlsSlots"{ row }"><div class"flexRow rowCenter"&…

重生之“我打数据结构,真的假的?”--3.栈和队列(无习题)

栈和队列 C语言中的栈和队列总结 在C语言中&#xff0c;**栈&#xff08;Stack&#xff09;和队列&#xff08;Queue&#xff09;**是两种非常重要的数据结构。它们广泛用于各种应用中&#xff0c;比如内存管理、任务调度、表达式求值等。本文将对这两种数据结构进行详细的介…

element ui中el-image组件查看图片的坑

比如说上传组件使用el-image-viewer组件去看&#xff0c;如果用错了&#xff0c;你会发现&#xff0c;你每次只能看一张图片 <template><div><el-upload action"#" list-type"picture-card" :auto-upload"false" :file-list"…

LTSC版本没有微软应用商店怎么办?一招装上

前言 这几天小白在办公电脑上安装了Windows 11 24H2 LTSC版本&#xff0c;哦豁&#xff0c;界面真的清爽。默认桌面上只有一个垃圾桶和EDGE浏览器&#xff0c;就再也没有其他图标了。 &#xff08;吐槽1️⃣&#xff1a;原版系统镜像开机之后不都是这样的吗&#xff1f;这也能…

Rust初踩坑

一、下载 到官网https://www.rust-lang.org/zh-CN/tools/install下载你需要的版本 二、安装 执行rustup-init 文件&#xff0c;选择1 按提示直到安装完成 可以通过以下命令测试&#xff1a; rustc -V # 注意的大写的 V cargo -V # 注意的大写的 V三、在VScode中…

GFF: Gated Fully Fusion for Semantic Segmentation门控融合语义分割-论文阅读笔记

摘要&#xff1a; 语义分割通过对每个像素密集预测其类别&#xff0c;生成对场景的全面理解。深度卷积神经网络的高级特征已经在语义分割任务中证明了它们的有效性&#xff0c;然而高级特征的粗分辨率经常导致对小/薄物体的结果不佳&#xff0c;而这些物体的细节信息非常重要。…

如何在算家云搭建GPT-SOVITS(语音转换)

一、模型介绍 GPT-SOVITS是一款强大的小样本语音转换和文本转语音 WebUI工具。它集成了声音伴奏分离、自动训练集分割、中文ASR和文本标注等辅助工具。 具有以下特征&#xff1a; 零样本 TTS&#xff1a; 输入 5 秒的声音样本并体验即时文本到语音的转换。少量样本 TTS&…

Rust的move关键字在线程中的使用

为什么使用 move&#xff1f; 在 Rust 中&#xff0c;move 关键字主要用于闭包。当我们在一个线程中创建一个闭包并将其传递给另一个线程时&#xff0c;如果闭包中使用了某些变量&#xff0c;就需要决定这些变量的所有权归属。 不使用 move&#xff1a; 默认情况下&#xff0…

python实战项目42:themoviedb电影网站信息

python爬取themoviedb电影网站信息 一、寻找数据接口二、解析主页数据,获取详情页url三、向详情页url发送请求、获取并解析数据四、完整代码一、寻找数据接口 打开网站首页,F12打开开发者工具,刷新页面。 向下滑动页面,点击页面上的“Load More”图标。 寻找到数据接口,…

【网络原理】TCP/IP五层网络模型之网络层-----IP协议详解,建议收藏!!

&#x1f490;个人主页&#xff1a;初晴~ &#x1f4da;相关专栏&#xff1a;计算机网络那些事 前几篇文章中我们深入研究了TCP协议&#xff0c;因为TCP协议在我们日常开发中的使用频率非常高。而相比之下&#xff0c;IP协议与我们普通程序员关系就没那么近了。一般是专门开发…