【问题标题】:JPA @PreUpdate is called for all child objects while adding new child object to parent在将新子对象添加到父对象时,为所有子对象调用 JPA @PreUpdate
【发布时间】:2018-06-05 21:00:11
【问题描述】:

我在父母和孩子之间有一对多的关系。 下面是关系的代码sn-p。

@Entity
@Table(name = "Parent")
public class Parent{

    @OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
    @Fetch(FetchMode.SELECT)
    @JoinColumn(name = "parentId",referencedColumnName="parentId")
    private Set<Child> child = new HashSet<>();
}

另外,在尝试保存和更新对象之前,我正在使用 @PreUpdate@PrePersist 对实体执行一些审计操作。

@PrePresist 看起来工作得很好。如文档中所述,在我尝试在数据存储中持久保存新对象之前调用它。

我在使用@PreUpdate 时遇到问题。如果父母有多个孩子,当我们尝试向它添加另一个孩子entityManager.merge(parent)@PrePresist 被正确调用,因为我们正在插入一个新的子记录,但它也为所有已存在于父级中的子级调用 @PreUpdate 注释方法,尽管任何子对象中都没有任何变化。它不应该为所有孩子调用PreUpdate,因为它们中没有任何更新。

有人知道为什么会这样吗?

提前致谢!

【问题讨论】:

    标签: java hibernate jpa hibernate-mapping


    【解决方案1】:

    这实际上是设计使然。对所有孩子调用PreUpdate 的原因是,在这种情况下,hibernate 首先删除所有孩子,然后重新插入它们。 http://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#associations-one-to-many-unidirectional 中描述了此行为。

    您遇到此问题是因为在这种情况下,父级拥有该关联。这意味着当保存父母时,父母和孩子之间的关联会在数据库中更新。除了删除所有子项并重新插入它们之外,没有其他方法可以做到这一点。

    有人可能会提出一种解决方案,即休眠跟踪对集合所做的更改,并仅插入已添加的记录并删除已删除的条目。虽然这似乎是一个解决方案,但如果关联已在数据库中更改,它将无法正常工作。

    问题的本质是,当客户端调用parent.save 时,它期望在保存完成后数据库将包含与parent 关联的子项的记录,这些记录在调用save 时位于children 集合中。

    如果 hibernate 只跟踪添加和删除并且只执行那些操作,那么以下场景将导致问题。

    假设父母已经与两个孩子相关联:

    Parent parent = new Parent();
    parent.children(asSet(child1, child2));
    parent.save();
    

    然后两个客户端同时从数据库中读取父状态。

    客户端 1:

    Parent parent = dao.getParent(parentId);
    

    客户端 2:

    Parent parent = dao.getParent(parentId);
    

    第一个客户端添加一个孩子并保存:

    parent.children().add(child3);
    parent.save();
    

    到目前为止一切顺利。但是第二个客户端仍然拥有只有child1child2 的父版本。例如,如果该客户端删除 child1 并保存:

    parent.children().remove(child1);
    parent.save();
    

    它期望 parent 现在只包含 child2 (因为这是保存时 children 集合包含的内容)。但是,如果我们使用跟踪实现并且这样的删除只会出现以下问题:

    DELETE FROM child where parent_id = <parent_id> and id = <child1_id>
    

    那么仍然会有两条记录与该父级关联 - child2child3

    希望这是有道理的,并解释了为什么 hibernate 会删除所有孩子并重新插入它们。

    要解决您的问题,您需要使用 bidirectional OneToMany 关联,以便关联的子方管理它。

    【讨论】:

    • 感谢罗曼的详细解释,这里只有一个疑问。我确实尝试通过启用 showsql=true 来检查应用程序日志,但在更新子记录时我没有看到任何删除查询。我只能看到所有子记录的更新查询。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-19
    • 2018-10-18
    • 2022-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多