Spring JPA中第二次查询未执行导致数据过时的原因?
问题原因及解决方案
核心原因:JPA一级缓存(EntityManager缓存)导致重复查询不访问数据库
你遇到的问题是**JPA一级缓存(EntityManager级别的会话缓存)**在起作用:
- 在
m2方法的同一个请求生命周期内,第一次调用findById(20L)时,JPA会把查询到的City实例存入当前EntityManager的一级缓存中。 - 第二次调用
findById(20L)时,JPA会优先从一级缓存中获取实例,不会发起新的SQL查询。 - 虽然你在断点期间通过
m1修改了数据库中的数据,但m1是独立的请求,使用的是另一个EntityManager,不会影响m2请求中的一级缓存。
另外,你配置的@Cacheable(false)和@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONE)是用来关闭二级缓存(跨会话的全局缓存)的,和本次问题无关——一级缓存是每个EntityManager自带的默认缓存,不受二级缓存配置影响。
解决方案
方案1:手动清空EntityManager缓存
在第二次查询前,清空当前EntityManager的一级缓存,强制JPA重新查询数据库:
@Autowired private EntityManager entityManager; @GetMapping("/m2") @Transactional(propagation = Propagation.NEVER) public Object m2(){ City city1 = cityRepository.findById(20L).orElseGet(null); // 清空当前EntityManager的一级缓存 entityManager.clear(); City city2 = cityRepository.findById(20L).orElseGet(null); return city2; }
方案2:让两次查询使用独立的EntityManager
去掉m2方法上的@Transactional(Propagation.NEVER)注解,这样每次调用Repository方法时,Spring Data JPA会自动创建独立的事务,事务结束后EntityManager会被关闭,缓存也会被清空:
@GetMapping("/m2") // 移除@Transactional(Propagation.NEVER) public Object m2(){ City city1 = cityRepository.findById(20L).orElseGet(null); // breakpoint here City city2 = cityRepository.findById(20L).orElseGet(null); return city2; }
方案3:自定义查询绕过一级缓存
在CityRepository中添加一个带查询提示的自定义方法,强制绕过缓存:
public interface CityRepository extends JpaRepository<City, Long> { @Query(value = "SELECT c FROM City c WHERE c.id = ?1", hints = @QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "false")) Optional<City> findByIdNoCache(Long id); }
然后在m2中第二次查询使用这个方法:
@GetMapping("/m2") @Transactional(propagation = Propagation.NEVER) public Object m2(){ City city1 = cityRepository.findById(20L).orElseGet(null); // breakpoint here City city2 = cityRepository.findByIdNoCache(20L).orElseGet(null); return city2; }
内容的提问来源于stack exchange,提问作者phantomsee
相关产品推荐
相关产品推荐

