Spring Boot整合Hibernate一级缓存两次调用findById仅打印1条SQL问题
问题原因分析
这个现象本质是 JPA 一级缓存(持久化上下文缓存)生效 导致的,具体逻辑如下:
- 一级缓存是 JPA 规范自带的
EntityManager级别的缓存,默认强制开启,生命周期和EntityManager实例绑定 - 当同一个
EntityManager实例查询同一个实体类、同一个主键 ID 的数据时,第一次查询会发送 SQL 到数据库,将返回的实体对象存入一级缓存;第二次查询时会直接从缓存中读取对象,不会再向数据库发送 SELECT 语句
你场景中两次调用复用了同一个 EntityManager 实例,所以触发了缓存逻辑,只打印了 1 条 SQL。
常见的 EntityManager 复用场景
你的代码里没有显式声明事务依旧触发缓存,通常是以下两种情况之一:
- 你的项目开启了 Open EntityManager in View(Spring Boot 中对应配置项
spring.jpa.open-in-view,默认是开启状态):该配置会把EntityManager的生命周期绑定到当前请求线程,整个请求处理过程中所有数据库操作都复用同一个EntityManager实例 - 你实际运行的代码中
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
相关产品推荐
相关产品推荐

