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

Spring Boot整合Hibernate一级缓存两次调用findById仅打印1条SQL问题

问题原因分析

这个现象本质是 JPA 一级缓存(持久化上下文缓存)生效 导致的,具体逻辑如下:

  • 一级缓存是 JPA 规范自带的 EntityManager 级别的缓存,默认强制开启,生命周期和 EntityManager 实例绑定
  • 当同一个 EntityManager 实例查询同一个实体类、同一个主键 ID 的数据时,第一次查询会发送 SQL 到数据库,将返回的实体对象存入一级缓存;第二次查询时会直接从缓存中读取对象,不会再向数据库发送 SELECT 语句

你场景中两次调用复用了同一个 EntityManager 实例,所以触发了缓存逻辑,只打印了 1 条 SQL。

常见的 EntityManager 复用场景

你的代码里没有显式声明事务依旧触发缓存,通常是以下两种情况之一:

  1. 你的项目开启了 Open EntityManager in View(Spring Boot 中对应配置项 spring.jpa.open-in-view,默认是开启状态):该配置会把 EntityManager 的生命周期绑定到当前请求线程,整个请求处理过程中所有数据库操作都复用同一个 EntityManager 实例
  2. 你实际运行的代码中 find() 方法的上层调用方加了 @Transactional 注解:事务范围内所有数据库操作都会复用同一个 EntityManager 实例

验证方式

你可以通过以下操作验证缓存逻辑,修改后会打印2条SELECT语句:

  • 方式1:在两次 findById 调用之间注入 EntityManager 并调用 clear() 方法清空一级缓存
@Service
@RequiredArgsConstructor
public class AccountService {
    private final AccountReporitory accountReporitory;
    private final EntityManager entityManager;

    public void find() {
        final Account account1 = accountReporitory.findById(1L).get();
        entityManager.clear(); // 清空一级缓存
        final Account account2 = accountReporitory.findById(1L).get();
    }
}
  • 方式2:关闭spring.jpa.open-in-view配置,同时确保find()方法没有被事务包裹
    在application.properties/application.yml中添加配置:
spring.jpa.open-in-view=false

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.20 01:15:03