@Transactional与@Modifying在Service层配合失效问题及咨询
问题解决与最佳实践分析
一、事务内更新后查询仍显示旧值的原因及解决方案
原因
同一个事务中,findById先把实体加载到JPA的**一级缓存(持久化上下文)**里,后续通过JPQL执行的更新是直接操作数据库,但缓存里的实体不会自动同步更新。所以findAll查询时,会优先从缓存取数据,导致看到旧值。
解决方案
给你几个可行的处理办法:
- 刷新指定实体:在更新后,注入
EntityManager并调用refresh方法刷新缓存里的目标实体@PersistenceContext private EntityManager entityManager; // 在updateName执行后添加 entityManager.refresh(e); - 自动清除缓存:给自定义更新方法的
@Modifying注解加上clearAutomatically = true,执行完更新后自动清空持久化上下文缓存@Modifying(clearAutomatically = true) @Query("UPDATE ExampleEntity e SET e.name = :name WHERE e.id IN (:ids)") int updateName(@Param("name") String name, @Param("ids") Collection<Long> ids); - 改用实体对象更新:既然已经查到了实体,直接修改属性再调用
save,JPA会自动同步数据库并更新缓存exampleRepository.findById(id).ifPresent(e -> { e.setName("NewName"); exampleRepository.save(e); // 后续findAll就能拿到新值 exampleRepository.findAll(); }); - 手动清空缓存:更新后调用
entityManager.clear()清空整个持久化上下文,后续查询会从数据库重新加载数据
二、Repository层@Transactional标注的合理性分析
不建议给整个Repository加@Transactional
Spring Data JPA对默认CRUD方法已经内置了事务管理规则:
- 查询类方法(
findById、findAll等)默认是只读事务,性能更优 - 修改类方法(
save、delete等)默认是读写事务
如果给整个Repository标注@Transactional,会覆盖这些默认配置,把所有方法都变成读写事务,既没必要还会降低查询性能。
正确做法:仅给自定义修改方法标注@Transactional
只有像updateName这种带@Modifying的自定义JPQL更新/删除方法,Spring Data JPA不会自动添加事务,需要单独给这类方法标注@Transactional,确保操作在事务内执行。
修改后的Repository代码示例:
@Repository interface ExampleRepository extends JpaRepository<Ex, Long> { @Modifying @Transactional @Query("UPDATE ExampleEntity e SET e.name = :name WHERE e.id IN (:ids)") int updateName(@Param("name") String name, @Param("ids") Collection<Long> ids); }
内容的提问来源于stack exchange,提问作者user10570720
相关产品推荐
相关产品推荐

