1. 概述

Spring JDBC和JPA在原生JDBC API之上提供了抽象层,让开发者可以告别原生SQL查询。但在调试时,我们经常需要查看这些自动生成的SQL语句及其执行顺序。

本教程将介绍在Spring Boot中记录SQL查询的几种方法。

2. 记录JPA查询

2.1. 输出到标准输出

最简单粗暴的方式是在application.properties中添加:

spring.jpa.show-sql=true

要美化SQL输出格式,可以再加一行:

spring.jpa.properties.hibernate.format_sql=true

配置后日志会这样显示:

2024-03-26T23:30:42.680-04:00 DEBUG 9477 --- [main] org.hibernate.SQL: 
    select
        c1_0.id,
        c1_0.budget,
        c1_0.end_date,
        c1_0.name,
        c1_0.start_date 
    from
        campaign c1_0 
    where
        c1_0.start_date between ? and ?

⚠️ 虽然简单,但不推荐这种方式:

  • 直接输出到标准输出,没有日志框架的优化
  • 不会记录预编译语句的参数

2.2. 通过日志框架

更专业的方式是配置日志记录器:

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

✅ 第一行记录SQL语句,第二行记录预编译参数
✅ 美化属性同样生效
✅ 日志会输出到配置的appender(默认是logback)

要记录带绑定参数的查询,添加:

logging.level.org.hibernate.orm.jdbc.bind=TRACE

这样会输出详细绑定信息:

org.hibernate.SQL : select c1_0.id,c1_0.budget,c1_0.end_date,c1_0.name,c1_0.start_date from campaign c1_0 where c1_0.start_date between ? and ?
org.hibernate.orm.jdbc.bind : binding parameter [1] as [DATE] - [2024-04-26]
org.hibernate.orm.jdbc.bind : binding parameter [2] as [DATE] - [2024-04-05]

3. 记录JdbcTemplate查询

使用JdbcTemplate时需要两个额外配置:

logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG
logging.level.org.springframework.jdbc.core.StatementCreatorUtils=TRACE

效果类似JPA配置:

  • 第一行记录SQL语句
  • 第二行记录预编译参数

输出示例:

2024-03-26T23:45:44.505-04:00 DEBUG 18067 --- [main] o.s.jdbc.core.JdbcTemplate: Executing prepared SQL statement [SELECT id FROM CAMPAIGN WHERE name = ?]
2024-03-26T23:45:44.513-04:00 TRACE 18067 --- [main] o.s.jdbc.core.StatementCreatorUtils: Setting SQL statement parameter value: column index 1, parameter value [sdfse1], value class [java.lang.String], SQL type unknown

4. 记录所有类型的查询

拦截器是记录所有SQL查询的最佳方案。通过拦截JDBC调用,可以自定义格式记录SQL。

推荐使用datasource-proxy库,先添加依赖:

<dependency>
    <groupId>com.github.gavlyukovskiy</groupId>
    <artifactId>datasource-proxy-spring-boot-starter</artifactId>
    <version>1.9.1</version>
</dependency>

然后启用日志:

logging.level.net.ttddyy.dsproxy.listener=debug

输出效果更专业:

Name:dataSource, Connection:15, Time:1, Success:True
Type:Prepared, Batch:False, QuerySize:1, BatchSize:0
Query:["select c1_0.id,c1_0.budget,c1_0.end_date,c1_0.name,c1_0.start_date from campaign c1_0 where c1_0.start_date between ? and ?"]
Params:[(2024-04-26,2024-04-05)]

拦截器能统一记录JPA、JPQL和预编译语句
✅ 是记录带绑定参数SQL的最佳实践

5. 原理揭秘

Spring/Hibernate中生成SQL和设置参数的类本身就包含日志代码,只是日志级别被设为DEBUGTRACE,低于Spring Boot默认的INFO级别。

我们添加的配置只是将这些日志记录器调整到所需级别,就这么简单。

6. 总结

本文介绍了Spring Boot中记录SQL查询的几种方式:

  1. 标准输出(简单但不推荐)
  2. 日志框架(推荐)
  3. 拦截器(最佳实践)

重点解决了预编译参数的记录问题,并说明了拦截器方案的优越性。

进阶技巧:通过配置多个appender,可以将SQL日志和其他日志分离到不同文件,保持日志整洁。


原始标题:Show Hibernate/JPA SQL Statements in Spring Boot | Baeldung