1. 概述
在这个教程中,我们将讨论P6Spy,一个开源的、用于Java应用程序中拦截SQL日志的免费库。
在文章的第一部分,我们将探讨使用这个外部库而非仅仅启用JPA或Hibernate的SQL日志记录的主要优势,以及我们如何将该库集成到我们的应用中。接着,我们将展示一个简单的例子,说明如何使用P6Spy与Spring Boot应用一起工作,以展示一些重要的配置选项。
2. P6Spy 安装
P6Spy需要在应用服务器上安装。通常,只需将应用JAR添加到类路径中,并适当地配置驱动程序和JDBC连接即可。
另一种使用P6Spy的方式是通过修改现有应用程序代码,假设进行小量代码更改是可以接受的。在下一节中,我们将看到如何通过依赖自动配置在Spring Boot应用中集成P6Spy的一个示例。
p6spy-spring-boot-starter是一个提供P6Spy和其他数据库监控库集成的存储库。通过这个库,启用P6Spy的日志记录非常简单,只需要在类路径上添加一个jar文件。使用Maven,只需在POM.xml
中添加以下内容:
<dependency>
<groupId>com.github.gavlyukovskiy</groupId>
<artifactId>p6spy-spring-boot-starter</artifactId>
<version>1.9.0</version>
</dependency>
如果我们想要配置日志,需要在资源文件夹中添加一个名为“spy.properties”的文件:
appender=com.p6spy.engine.spy.appender.FileLogger
logfile=database.log
append=true
logMessageFormat=com.p6spy.engine.spy.appender.CustomLineFormat
customLogMessageFormat=%(currentTime)|%(executionTime)|%(category)|%(sqlSingleLine)
在这个例子中,我们已配置P6Spy以以追加模式将信息写入名为“database.log”的自定义格式文件。这只是其中的一些配置,更多详细信息可以在项目网站上找到。
3. 日志示例
为了查看日志,我们需要运行一些查询。让我们在应用中添加几个简单的端点:
@RestController
@RequestMapping("student")
public class StudentController {
@Autowired
private StudentRepository repository;
@RequestMapping("/save")
public Long save() {
return repository.save(new Student("Pablo", "Picasso")).getId();
}
@RequestMapping("/find/{name}")
public List<Student> getAll(@PathVariable String name) {
return repository.findAllByFirstName(name);
}
}
假设应用暴露在8080端口上,现在我们可以使用CURL来访问这些端点:
curl http://localhost:8080/student/save
curl http://localhost:8080/student/find/Pablo
现在我们可以看到,项目目录下已创建了一个名为“database.log”的文件,其内容如下:
1683396972301|0|statement|select next value for student_seq
1683396972318|0|statement|insert into student (first_name, last_name, id) values ('Pablo', 'Picasso', 1)
1683396972320|0|commit|
1683396990989|0|statement|select s1_0.id,s1_0.first_name,s1_0.last_name from student s1_0 where s1_0.first_name='Pablo'
如果使用预编译语句,并手动管理提交和回滚,日志记录也会起作用。让我们在应用中添加另一个控制器来测试此行为:
@RestController
@RequestMapping("jdbc")
public class JDBCController {
@Autowired
private DataSource dataSource;
@RequestMapping("/commit")
public List<Map<String, String>> select() {
List<Map<String, String>> results = new ArrayList<>();
try {
Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
statement.executeQuery("SELECT * FROM student");
} catch (Exception e) {
throw new IllegalStateException(e);
}
return results;
}
@RequestMapping("/rollback")
public List<Map<String, String>> rollback() {
List<Map<String, String>> results = new ArrayList<>();
try (Connection connection = dataSource.getConnection()) {
connection.rollback();
} catch (Exception e) {
throw new IllegalStateException(e);
}
return results;
}
@RequestMapping("/query-error")
public void error() {
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement()) {
statement.execute("SELECT UNDEFINED()");
} catch (Exception ignored) {
}
}
}
然后,通过CURL请求访问以下端点:
curl http://localhost:8080/jdbc/commit
curl http://localhost:8080/jdbc/rollback
curl http://localhost:8080/jdbc/query-error
结果,我们会在“database.log”文件中看到以下日志条目:
1683448381083|0|statement|SELECT * FROM student
1683448381087|0|commit|
1683448386586|0|rollback|
1683448388604|3|statement|SELECT UNDEFINED()
4. 总结
在这篇文章中,我们看到了依赖像P6Spy这样的第三方库来记录数据库查询的多个优势。例如,我们尝试的特定配置可以避免“吵闹邻居”问题(想象一下满是查询的日志控制台)。
如往常一样,完整的代码可在GitHub上找到。