Spring Data JPA执行findOne未获取数据库历史值疑似缓存问题
问题根因
这是JPA 持久化上下文(一级缓存) 的正常运行逻辑:
- 同一个
EntityManager(持久化上下文)内如果存在对应ID的受管实体(也就是你已经修改过的cuentaDetalle对象),执行findOne/findById方法时会直接返回这份受管对象的引用,不会重新发起SQL查询数据库。 - 只要
cuentaDetalle处于受管状态,你对它属性的修改会直接作用于持久化上下文内的这份实例,后续查询拿到的就是修改后的值,而非数据库存储的历史值。 - 你当前的代码逻辑中,
findOne返回的detOld和cuentaDetalle极有可能是同一个对象引用,所以两者的所有属性完全一致,无法实现新旧值对比的需求。
可行解决方案
1. 提前查询旧值
在修改cuentaDetalle的任何属性之前,先调用findOne拿到旧值快照,后续再执行修改、对比操作。
这种方案最简便,不需要额外操作持久化上下文,适合修改动作发生在查询旧值之后的场景,调整后代码顺序参考:// 优先查询旧值,此时cuentaDetalle未被修改 CuentaDetalle detOld = findOne(cuentaDetalle.getId()); // 后续再执行任何修改cuentaDetalle属性的操作2. 分离当前修改的实体
先把修改后的cuentaDetalle从持久化上下文转为游离状态,再执行查询即可拿到数据库的旧值:@PersistenceContext private EntityManager entityManager; // 分离当前修改后的实体 entityManager.detach(cuentaDetalle); // 再次查询会从数据库加载未被当前修改污染的旧值 CuentaDetalle detOld = findOne(cuentaDetalle.getId());3. 清空持久化上下文
如果需要批量清空当前所有受管实体的缓存,可以调用EntityManager.clear()方法,清空后所有实体转为游离状态,后续查询会重新走数据库:注意:该操作会丢弃当前持久化上下文内所有未同步到数据库的修改,使用前需要确认没有需要暂存的受管实体改动。
// 清空持久化上下文 entityManager.clear(); // 此时查询会直接返回数据库存储的旧值 CuentaDetalle detOld = findOne(cuentaDetalle.getId());4. 自定义原生查询强制走库
可以在Repository中定义自定义查询方法,直接编写JPQL/原生SQL查询,不过该方式仍会受一级缓存影响,需要配合detach或clear使用:// Repository中定义查询 @Query("SELECT c FROM CuentaDetalle c WHERE c.id = :id") CuentaDetalle queryByIdFromDb(@Param("id") Long id);
内容的提问来源于stack exchange,提问作者NicoGuevaraAtuq
相关产品推荐
相关产品推荐

