Spring Boot 3中repository.save()未触发@PreUpdate问题咨询
问题分析与解答
核心原因:脏检查机制导致@PreUpdate未触发
没错,这就是Hibernate/JPA脏检查机制的正常行为:
- Hibernate的脏检查仅追踪映射到数据库列的持久化属性,
@Transient修饰的属性会被完全排除在持久化上下文的追踪范围之外。 - 当你修改
@Transient的data集合后调用save(),Hibernate检测不到任何持久化属性的变化,会判定该实体“未脏化”,因此不会执行数据库UPDATE操作,自然也不会触发@PreUpdate标注的onSave()方法。
手动调用onSave()执行两次的原因
你手动调用onSave()后出现两次执行,通常是因为:
- 第一次是你主动调用的;
- 第二次是因为
onSave()方法内部可能修改了实体的某个持久化属性(比如更新时间戳字段),导致Hibernate脏检查检测到变化,触发了更新流程,进而自动调用了@PreUpdate方法。
或者,若事务在手动调用后触发刷新(比如事务提交),即使onSave()没修改持久化属性,只要实体因其他原因被判定为脏,也会触发一次自动调用。
解决方案
如果需要在修改@Transient属性时触发@PreUpdate,可以参考以下方式:
方式1:手动标记实体为脏
通过EntityManager强制标记实体为脏,触发更新流程:
entityManager.merge(someJson); // 或者用任意持久化属性触发脏检查的方式 entityManager.getEntityManagerFactory().getPersistenceUnitUtil().setProperty(someJson, "id", someJson.getId());
这样Hibernate会判定实体已脏,执行更新并触发@PreUpdate。
方式2:添加触发用的持久化属性
在实体类中新增一个无关的持久化属性(比如lastUpdated),每次修改@Transient的data集合时同步更新该属性:
@Entity public class SomeJson { // 其他属性... @Column private LocalDateTime lastUpdated; @Transient private List<Object> data; public void updateData(List<Object> newData) { this.data = newData; this.lastUpdated = LocalDateTime.now(); // 触发脏检查 } }
修改data后,脏检查会检测到lastUpdated的变化,自动触发更新和@PreUpdate。
方式3:避免重复调用的手动执行
如果选择手动调用onSave(),需要确保:
- 只手动调用一次;
- 方法内部不要修改任何持久化属性,避免触发Hibernate的自动
@PreUpdate调用。
内容的提问来源于stack exchange,提问作者injecteer
相关产品推荐
相关产品推荐

