@Transactional是否维持会话开启?为何重复调用查询方法触发3次DB调用?
@Transactional场景下三次DB调用原因说明
你对Spring事务绑定Hibernate会话的认知是正确的:标注@Transactional的测试方法运行期间,所有数据库操作确实共享同一个持久化上下文(即Hibernate Session),一级缓存(会话级缓存)也处于生效状态,出现三次DB调用的核心原因是Hibernate一级缓存的生效范围有明确限制,你当前的场景不在其覆盖范围内:
- 一级缓存仅会缓存通过主键查询得到的实体对象,也就是调用
EntityManager.find()、JpaRepository.findById()这类主键查询方法返回的实体,会被存入一级缓存,同会话内重复同主键查询不会再访问数据库。 - 你调用的
dishRepository.findAll()本质是执行了一次全表查询的JPQL语句,Hibernate默认不会缓存任意JPQL/SQL的查询结果,每次执行这类查询都会直接访问数据库,哪怕同一会话、参数完全一致也会重复打库。
如果需要实现同事务内重复执行相同查询语句不访问数据库,可以开启Hibernate查询缓存,步骤如下:
- 在application配置文件中开启查询缓存全局开关:
spring.jpa.properties.hibernate.cache.use_query_cache = true
- 给对应的查询方法添加缓存提示,标注该查询结果可缓存:
@QueryHints(value = @QueryHint(name = org.hibernate.jpa.QueryHints.HINT_CACHEABLE, value = "true")) List<Dish> findAll();
注意:查询缓存仅对语句和参数完全相同的查询生效,且同会话内如果有对查询涉及表的写入操作,会自动清空对应表的查询缓存避免脏读。
你可以做个简单验证:在当前的事务测试方法中,两次调用dishRepository.findById(同一个存在的主键id),只会产生1次DB查询,这就是一级缓存正常生效的场景。
内容的提问来源于stack exchange,提问作者user16722881
相关产品推荐
相关产品推荐

