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

如何在Spring Boot中记录SQL查询耗时?

解决方案:Spring/Hikari内置实现SQL耗时统计

一、利用Spring JDBC的StatementInterceptor拦截SQL执行

Spring JDBC原生提供StatementInterceptor接口,可在SQL执行前后插入逻辑,正好用来统计耗时,还能结合Log4j2的开关灵活控制。

实现步骤:

  1. 自定义拦截器类:
import org.springframework.jdbc.core.StatementInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.util.Properties;

public class SqlTimingInterceptor implements StatementInterceptor {
    private static final Logger logger = LoggerFactory.getLogger("SqlTimingLogger");

    @Override
    public Statement intercept(Statement statement, Connection connection) {
        String sql = null;
        // 尝试从PreparedStatement中解析SQL语句(兼容不同JDBC驱动格式)
        if (statement instanceof PreparedStatement) {
            try {
                sql = ((PreparedStatement) statement).toString().split(": ")[1];
            } catch (Exception e) {
                sql = "无法解析SQL内容";
            }
        }
        return new TimingStatement(statement, sql);
    }

    @Override
    public void setProperties(Properties properties) {}

    // 包装Statement,记录执行前后时间差
    private static class TimingStatement extends StatementWrapper {
        private final String sql;
        private long startTime;

        public TimingStatement(Statement delegate, String sql) {
            super(delegate);
            this.sql = sql;
        }

        @Override
        public boolean execute(String sql) throws SQLException {
            startTime = System.currentTimeMillis();
            try {
                return super.execute(sql);
            } finally {
                logExecutionTime(sql);
            }
        }

        @Override
        public int executeUpdate(String sql) throws SQLException {
            startTime = System.currentTimeMillis();
            try {
                return super.executeUpdate(sql);
            } finally {
                logExecutionTime(sql);
            }
        }

        @Override
        public java.sql.ResultSet executeQuery(String sql) throws SQLException {
            startTime = System.currentTimeMillis();
            try {
                return super.executeQuery(sql);
            } finally {
                logExecutionTime(sql);
            }
        }

        @Override
        public boolean execute() throws SQLException {
            startTime = System.currentTimeMillis();
            try {
                return super.execute();
            } finally {
                logExecutionTime(this.sql);
            }
        }

        private void logExecutionTime(String sql) {
            if (logger.isDebugEnabled()) {
                long elapsed = System.currentTimeMillis() - startTime;
                logger.debug("SQL执行耗时: {}ms | SQL内容: {}", elapsed, sql);
            }
        }
    }
}
  1. 配置到Spring的JdbcTemplate:
  • Java配置方式:
@Configuration
public class JdbcConfig {
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        jdbcTemplate.setStatementInterceptors(new SqlTimingInterceptor());
        return jdbcTemplate;
    }
}
  • XML配置方式:
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="hikariDataSource"/>
    <property name="statementInterceptors">
        <list>
            <bean class="com.yourpackage.SqlTimingInterceptor"/>
        </list>
    </property>
</bean>
  1. Log4j2开关控制:
    在log4j2.xml中配置对应日志级别,按需开启:
<Logger name="SqlTimingLogger" level="DEBUG" additivity="false">
    <AppenderRef ref="YourAppender"/>
</Logger>

二、HikariCP内置慢SQL日志(零代码配置)

HikariCP本身支持慢SQL记录,无需额外代码,只需配置参数即可:

Spring Boot环境配置示例:

在application.properties中添加:

# 慢SQL阈值,单位毫秒,超过该时长的SQL会被记录
spring.datasource.hikari.leak-detection-threshold=500
# 开启Hikari的DEBUG日志,输出连接池及慢SQL信息
logging.level.com.zaxxer.hikari=DEBUG

注:若仅需慢SQL记录,开启leak-detection-threshold即可;DEBUG日志会输出更多连接池细节,可根据需求调整级别。

三、修复你之前的LogWriter方案

你之前的setLogWriter未生效,是因为HikariCP默认关闭了JDBC LogWriter功能,调整配置即可启用:

步骤:

  1. 开启Hikari的LogWriter配置:
spring.datasource.hikari.logWriterEnabled=true
  1. 修改代码确保操作HikariDataSource实例:
@PostConstruct
public void setLogWriter() {
    final Logger logger = LoggerFactory.getLogger("no.bring.notifications.engine.JdbcStatements");

    try {
        if (dataSource instanceof HikariDataSource) {
            HikariDataSource hikariDs = (HikariDataSource) dataSource;
            hikariDs.setLogWriter(new PrintWriter(System.out) {
                @Override
                public void println(String x) {
                    if (logger.isDebugEnabled()) {
                        logger.debug(x);
                    }
                }
            });
        }
    } catch (SQLException e) {
        logger.error("设置日志写入器失败", e);
    }
}

此方案仅能输出Hikari连接池相关日志,无法直接统计SQL执行耗时,更推荐前两种方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 17:02:39