修改一对多集合中子实体数据时,@PostUpdate是否应触发?
Hibernate @PostUpdate不触发的问题解决
问题原因
JPA的@PostUpdate回调仅在实体自身的持久化状态发生变更时触发。当修改子实体Bar的字段时,只有Bar的状态被标记为脏数据,父实体Foo本身的字段无任何变更,Hibernate判定Foo无需更新,因此不会触发FooListener中的@PostUpdate方法。
解决方案
方案1:给父实体添加状态追踪字段
通过给Foo添加@Version乐观锁字段或更新时间字段,修改子实体时同步更新该字段,让Hibernate检测到Foo的状态变更。
修改Foo实体:
@EntityListeners({FooListener.class}) @Entity public class Foo { @Id long id; @Version private int version; @OneToMany(orphanRemoval = true, cascade = CascadeType.ALL) @Getter List<Bar> bars; public void touch() { this.version++; } }
测试代码中修改子实体后调用touch():
transactionTemplate.executeWithoutResult( tx -> { var foo = fooRepository.findById(idRef.get()).orElseThrow(); foo.bars.get(0).data = "Z"; foo.touch(); // 标记Foo为脏数据 fooRepository.save(foo); });
方案2:监听子实体的更新事件
给Bar添加实体监听器,在子实体更新时执行原本要在Foo的@PostUpdate中处理的逻辑。
创建BarListener:
@Component public class BarListener { private static final Logger log = LoggerFactory.getLogger(BarListener.class); @PostUpdate public void afterBarUpdate(Bar bar) { log.debug("Bar updated, trigger Foo-related logic"); // 可在此处操作关联的Foo实体,执行对应业务逻辑 Foo foo = bar.getFoo(); // ... } }
给Bar绑定监听器:
@EntityListeners({BarListener.class}) @Entity public class Bar { @Id long id; @ManyToOne(optional=false) Foo foo; @Getter @Setter String data; }
方案3:手动标记父实体为脏数据
使用Hibernate的EntityManager手动将Foo标记为脏数据,强制触发更新(不推荐,仅作兜底方案):
@Autowired EntityManager entityManager; transactionTemplate.executeWithoutResult( tx -> { var foo = fooRepository.findById(idRef.get()).orElseThrow(); foo.bars.get(0).data = "Z"; entityManager.markDirty(foo); // Hibernate 5.2+支持 fooRepository.save(foo); });
内容的提问来源于stack exchange,提问作者Archimedes Trajano
相关产品推荐
相关产品推荐

