Spring Data JPA持久化上下文出现不符合预期的行为
为什么Spring Data JPA持久化上下文未缓存自定义查询结果?
我定义了如下CategoryRepository:
public interface CategoryRepository extends JpaRepository<Category, Long>{ @Transactional @Query("select c from Category c where name = :name") Optional<Category> findByName(String name); }
对应的测试用例:
@SpringBootTest @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class CategoryRepositoryTests { @Autowired private CategoryRepository categoryRepository; @Test @Transactional public void testCaching(){ categoryRepository.findByName("cat1"); categoryRepository.findByName("cat1"); } }
数据库中始终存在名为cat1的Category数据。我预期首次调用categoryRepository.findByName("cat1");后,实体会存入持久化上下文,二次调用不会触发数据库查询,但实际二次调用仍执行了SQL查询。
问题原因
JPA的一级缓存(持久化上下文)默认基于实体ID做缓存匹配,而自定义JPQL查询(如基于name字段的查询)不会自动触发持久化上下文的检查逻辑。即使同一事务内重复执行查询,JPA仍会直接发送SQL到数据库,而非先检查缓存中是否存在匹配实体。
解决方法
1. 添加查询提示启用一级缓存检查
在自定义查询上添加@QueryHints,强制JPA先检查持久化上下文:
@Transactional @Query("select c from Category c where name = :name") @QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHE_RETRIEVE_MODE, value = "USE")) Optional<Category> findByName(String name);
添加后,JPA会优先在当前事务的持久化上下文中查找匹配实体,仅当找不到时才执行数据库查询。
2. 启用二级缓存(跨事务场景)
如果需要在多个事务间复用查询结果,可配置JPA二级缓存:
- 给实体类添加缓存注解:
@Entity @Cacheable @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY) public class Category { // 实体字段定义 }
- 在Repository方法上添加Spring缓存注解:
@Transactional @Query("select c from Category c where name = :name") @Cacheable("categories") Optional<Category> findByName(String name);
3. 优先使用ID查询(若场景允许)
如果能提前获取实体ID,优先调用findById方法——该方法会自动利用一级缓存,避免重复查询数据库。
内容的提问来源于stack exchange,提问作者Cody
相关产品推荐
相关产品推荐

