@PreUpdate在更新时不保存父对象

时间:2017-08-14 16:54:51

标签: java spring hibernate jpa spring-data

我有两个关系一对多的实体。 Parent 可以有多个 Child 实体实例。 我向父母添加了一个字段,用于存储子修改的日期( childrenLastModifiedDate )。为了保持这一点,我添加了方法:

@PrePersist
@PreUpdate
@PreRemove
private void handle() {
    parent.setChildrenLastModifiedDate(now());
}

这是问题所在。保存孩子时并不总是调用它。 在本地(mac os),它按预期工作,跟踪所有三种类型的更改并保存到父实体。但是,在服务器(linux)上它只适用于:

  • @PrePersist
  • @PreRemove

即使调用PreUpdate,也不会保存更改。我甚至尝试添加直接存储库调用来保存父级。结果是一样的。更新时不会保存任何内容,但会在删除或保留时保存。 我尝试在额外的事务中进行此更改并且它有效,但它太耗费资源。此外,我可以看到有人有非常相似的经历:

JPA/Hibernate preUpdate doesn't update parent object

然而,如何处理问题本身并没有什么。 问题是:有没有办法保证使用此注释始终有效并执行依赖实体的其他更新?如果不是,那么处理这种逻辑的最佳方法是什么?对儿童的每次更改都需要更新。即使它是通过级联保存的。

2 个答案:

答案 0 :(得分:1)

您的问题似乎仅适用于@PreUpdate

我的个人意见是如果未调用@PreUpdate ,则您的实体未更新。但你可以很容易地检查它。我建议对需要检查update活动的实体使用乐观锁(仅使用version field following hibernate doc)或auditing。如果处理了更新,您肯定可以检测是否处理了@PreUpdate活动。经过一些测试后,您将发现问题所在。

P.S。

如果同时为实体使用@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