Spring Data JPA原生更新方法JUnit测试字段未更新问题
问题根因
这个问题是Spring Data JPA自定义修改操作和一级缓存机制冲突导致的,核心逻辑:
你调用repository.save()后,savedEnt实体实例已经被Hibernate存入持久化上下文(一级缓存)。后续执行的@Modifying原生UPDATE语句是直接操作数据库,默认不会同步更新、也不会清理一级缓存里的旧实体。当你再调用查询方法查同ID数据时,Hibernate优先返回一级缓存里已经存在的旧对象,根本不会去数据库读取更新后的值,所以拿到的restrictionLevel始终是初始的null。
少数场景下还会因为实体字段映射错误、更新语句未真正flush到库导致问题。
解决步骤
按以下顺序排查修复即可:
- 给
@Modifying注解补充缓存清理配置
这是最一劳永逸的改法,直接修改Repository层的更新方法注解:
两个参数作用:@Modifying(flushAutomatically = true, clearAutomatically = true) @Query(value = "UPDATE ENTITLEMENT set RESTRICTION_LEVEL=:restrictionLevel where ID =:entitlementId", nativeQuery = true) void updateRestrictionLevel(@Param("restrictionLevel") String restrictionLevel, @Param("entitlementId") Long entitlementId);flushAutomatically = true:执行更新前先把持久化上下文里所有待提交的变更刷到数据库,避免前置save操作未实际落库就执行更新clearAutomatically = true:更新执行完成后自动清空一级缓存,后续查询会强制访问数据库读取最新值,不会返回缓存旧对象
- 不想修改Repository代码的话,可以在测试类里手动控制缓存
在执行更新方法后,调用TestEntityManager清空缓存再做查询:repository.updateRestrictionLevel("LEVEL11", savedEnt.getId()); entityManager.clear(); // 清空一级缓存,强制后续查询走数据库 Entitlement updatedEnt = repository.findByIdAndActiveTrue(savedEnt.getId()); - 检查实体类字段映射配置
确认Entitlement类中restrictionLevel字段的@Column注解配置正确:- 映射的列名必须和数据库表一致,即
RESTRICTION_LEVEL - 不能配置
updatable = false属性,否则JPA会阻止该字段的更新操作
错误配置示例:
// 该配置会导致字段无法被更新 @Column(name = "RESTRICTION_LEVEL", updatable = false) private String restrictionLevel; - 映射的列名必须和数据库表一致,即
- 打开SQL日志排查语句执行问题
在test环境配置中添加以下参数,打印JPA执行的完整SQL和参数,确认UPDATE语句是否正常发送、参数绑定是否正确:spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
补充说明:
@DataJpaTest默认会在测试方法结束后回滚事务,这个机制不会影响方法内的更新和查询逻辑,不需要额外添加@Rollback(false)配置。
内容的提问来源于stack exchange,提问作者Rahul
相关产品推荐
相关产品推荐

