baeldung 2018-04-09
1. 概述
Spring JDBC 和 JPA 对原生 JDBC API 提供了抽象层,使开发者无需编写原生 SQL 查询。然而,在调试时,我们经常需要查看这些自动生成的 SQL 查询及其执行顺序。
在本篇快速教程中,我们将探讨在 Spring Boot 中记录这些 SQL 查询的多种方法。
2. 记录 JPA 查询
2.1. 输出到标准输出(Standard Output)
最简单的方式是将以下配置添加到 application.properties 文件中,将 SQL 查询直接打印到标准输出:
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 ?
虽然这种方法非常简单,但不推荐使用,因为它直接将所有内容输出到标准输出,而没有利用日志框架的任何优化功能。
此外,它不会记录预编译语句(Prepared Statement)中的参数值。
2.2. 通过日志记录器(Loggers)
我们也可以通过在配置文件中设置日志级别来记录 SQL 语句:
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
- 第一行用于记录 SQL 查询;
- 第二行用于记录预编译语句的参数绑定信息。
上述配置同样支持 format_sql 美化功能。
通过这些配置,日志将被发送到已配置的 appender。Spring Boot 默认使用 Logback,并附带一个标准输出 appender。
如果我们希望记录带有绑定参数的查询,还可以在 application.properties 中添加以下属性:
logging.level.org.hibernate.orm.jdbc.bind=TRACE
该属性将 Hibernate 的 JDBC 绑定日志级别设为 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 时,需要额外配置两个属性来启用 SQL 日志记录:
logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG
logging.level.org.springframework.jdbc.core.StatementCreatorUtils=TRACE
- 第一行用于记录 SQL 语句;
- 第二行用于记录预编译语句的参数。
配置后,日志输出将包含 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. 记录所有类型的查询
使用拦截器(Interceptor) 是记录所有类型 SQL 查询的最佳方式。通过拦截 JDBC 调用,我们可以格式化并以自定义方式记录 SQL 查询。
datasource-proxy 是一个流行的用于拦截和记录 SQL 查询的库。首先,在 pom.xml 中添加其依赖:
<dependency>
<groupId>com.github.gavlyukovskiy</groupId>
<artifactId>datasource-proxy-spring-boot-starter</artifactId>
<version>1.12.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 和 PreparedStatement 的所有查询,因此是记录带参数 SQL 查询的最佳方案。
5. 工作原理
Spring/Hibernate 中生成 SQL 语句并设置参数的类本身已经包含了日志记录代码。
但这些日志的级别被设置为 DEBUG(SQL)和 TRACE(参数),低于 Spring Boot 默认的日志级别(INFO)。
通过上述配置,我们只是将相关日志器的级别调整为所需级别,从而启用这些日志输出。
6. 结论
在本文中,我们介绍了在 Spring Boot 中记录 SQL 查询的几种方法,并探讨了如何记录 SQL 查询的绑定参数。最后,我们说明了为什么拦截器方式是记录带参数 SQL 查询的最佳选择。
如果项目中配置了多个 appender,还可以将 SQL 语句与其他日志分别输出到不同的日志文件中,以保持日志结构的清晰整洁。