我有两个关系一对多的实体。 Parent 可以有多个 Child 实体实例。 我向父母添加了一个字段,用于存储子修改的日期( childrenLastModifiedDate )。为了保持这一点,我添加了方法:
@PrePersist
@PreUpdate
@PreRemove
private void handle() {
parent.setChildrenLastModifiedDate(now());
}
这是问题所在。保存孩子时并不总是调用它。 在本地(mac os),它按预期工作,跟踪所有三种类型的更改并保存到父实体。但是,在服务器(linux)上它只适用于:
即使调用PreUpdate,也不会保存更改。我甚至尝试添加直接存储库调用来保存父级。结果是一样的。更新时不会保存任何内容,但会在删除或保留时保存。 我尝试在额外的事务中进行此更改并且它有效,但它太耗费资源。此外,我可以看到有人有非常相似的经历:
JPA/Hibernate preUpdate doesn't update parent object
然而,如何处理问题本身并没有什么。 问题是:有没有办法保证使用此注释始终有效并执行依赖实体的其他更新?如果不是,那么处理这种逻辑的最佳方法是什么?对儿童的每次更改都需要更新。即使它是通过级联保存的。
答案 0 :(得分:1)
您的问题似乎仅适用于@PreUpdate
。
我的个人意见是如果未调用@PreUpdate ,则您的实体未更新。但你可以很容易地检查它。我建议对需要检查update
活动的实体使用乐观锁(仅使用version field following hibernate doc)或auditing
。如果处理了更新,您肯定可以检测是否处理了@PreUpdate
活动。经过一些测试后,您将发现问题所在。
如果同时为实体使用@PreUpdate
并为实体订阅@EntityListeners
,我从未检查过行为。至于我,在这种情况下可能会发生冲突。不确定,但如果你有这两个注释,请检查复杂和单独的行为(谁知道,可能是hibernate开发人员决定不可能的情况,因为如果在你的实体监听器中声明了一些,那么实体中不需要@PreUpdate
个处理程序如果您要检查,请在评论中告诉我。
答案 1 :(得分:1)
我遇到了同样的问题。我通过使用休眠拦截器对其进行了修复。
首先,您必须在基础实体类中声明一些基础方法,使我们能够找到实体的父级,这是一种用于链接更新所需字段的方法。
public abstract class BaseEntity {
public Optional<BaseEntity> getParent() {
// subclass should override this method to return the parent entity
return Optional.empty();
}
public void updateChain() {
// update your desired fields
...
this.getParent().ifPresent(p -> p.updateChain());
}
}
然后,您可以使用以下Hibernate拦截器强制更新脏实体的所有父代。
public class EntityChainForceUpdateHibernateInterceptor extends EmptyInterceptor {
@Override
public void preFlush(Iterator entities) {
entities.forEachRemaining(e -> {
if (BaseEntity.class.isAssignableFrom(e.getClass())) {
BaseEntity b = (BaseEntity) e;
b.getParent().ifPresent(p -> p.updateChain());
}
});
}
}
要在Spring中注册此拦截器,只需将以下行添加到application.properties
spring.jpa.properties.hibernate.ejb.interceptor=com.your.package.EntityChainForceUpdateHibernateInterceptor