You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

生产环境无需堆转储查看JDBC Prepared Statements列表方法问询

如何在生产环境查看Java应用JDBC端的Prepared Statements列表(无需堆转储)

方法1:利用PostgreSQL JDBC驱动内部缓存(反射方式)

PostgreSQL JDBC驱动(pgjdbc)会在连接内部维护Prepared Statement缓存,可通过反射获取缓存中的SQL语句列表,适合快速排查:

import org.postgresql.jdbc.PgConnection;
import java.sql.Connection;
import java.lang.reflect.Field;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PreparedStmtLogger {
    private static final Logger logger = LoggerFactory.getLogger(PreparedStmtLogger.class);

    public static void logPreparedStatements(Connection conn) {
        if (!(conn instanceof PgConnection)) {
            logger.warn("Connection is not a PostgreSQL JDBC connection");
            return;
        }

        PgConnection pgConn = (PgConnection) conn;
        try {
            // 获取驱动内部的PreparedStatement缓存(字段名随驱动版本可能变化)
            Field cacheField = PgConnection.class.getDeclaredField("preparedStatementCache");
            cacheField.setAccessible(true);
            Map<String, ?> stmtCache = (Map<String, ?>) cacheField.get(pgConn);

            // 遍历缓存并写入日志
            logger.info("Current JDBC Prepared Statement Cache (size: {})", stmtCache.size());
            for (String sql : stmtCache.keySet()) {
                logger.info("Cached SQL: {}", sql);
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            logger.error("Failed to access Prepared Statement cache", e);
        }
    }
}

注意:该方式依赖驱动内部实现,不同pgjdbc版本的字段名可能变更,建议先确认对应版本的源码结构。

方法2:自定义PreparedStatement包装器(连接池拦截)

如果使用连接池(如HikariCP、Druid),可以通过包装Connection对象,拦截所有prepareStatement调用,记录创建的预编译语句:

以HikariCP为例,自定义连接包装类:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TrackingConnection implements Connection {
    private static final Logger logger = LoggerFactory.getLogger(TrackingConnection.class);
    private final Connection delegate;

    public TrackingConnection(Connection delegate) {
        this.delegate = delegate;
    }

    // 拦截prepareStatement方法,记录SQL
    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        logger.info("Created Prepared Statement: {}", sql);
        return delegate.prepareStatement(sql);
    }

    // 实现Connection接口的所有其他方法,全部委托给delegate
    @Override
    public void close() throws SQLException {
        delegate.close();
    }

    // 其余方法(如createStatement、prepareCall等)请通过IDE自动生成委托实现
}

然后在连接池配置中添加拦截逻辑,比如HikariCP可以通过自定义HikariDataSource的setConnectionInitSql或扩展PoolEntryCreator,将原生Connection包装为TrackingConnection。

方法3:通过JMX监控获取

多数连接池和pgjdbc驱动支持JMX暴露监控指标,可通过JMX API获取Prepared Statement缓存信息:

import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JmxStmtMonitor {
    private static final Logger logger = LoggerFactory.getLogger(JmxStmtMonitor.class);

    public static void logStmtCacheStats() {
        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
        try {
            // 查询pgjdbc的连接池MBean
            Set<ObjectName> connPoolBeans = mBeanServer.queryNames(
                new ObjectName("org.postgresql:type=ConnectionPool,*"), null
            );

            for (ObjectName beanName : connPoolBeans) {
                Integer cacheSize = (Integer) mBeanServer.getAttribute(beanName, "PreparedStatementCacheSize");
                logger.info("Connection Pool {} - Prepared Statement Cache Size: {}", beanName, cacheSize);
                // 部分驱动版本可能支持获取缓存的SQL列表,可尝试获取对应属性
            }

            // 若使用HikariCP,可查询其MBean获取缓存命中率等信息
            Set<ObjectName> hikariBeans = mBeanServer.queryNames(
                new ObjectName("com.zaxxer.hikari:type=Pool,*"), null
            );
            for (ObjectName beanName : hikariBeans) {
                Double stmtCacheHitRate = (Double) mBeanServer.getAttribute(beanName, "PreparedStatementCacheHitRatio");
                logger.info("Hikari Pool {} - Prepared Statement Cache Hit Rate: {:.2%}", beanName, stmtCacheHitRate);
            }
        } catch (Exception e) {
            logger.error("Failed to fetch JMX stats", e);
        }
    }
}

生产环境也可直接用JConsole、VisualVM连接应用的JMX端口,实时查看缓存状态,无需修改代码。


内容的提问来源于stack exchange,提问作者Peter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 04:42:48