【问题标题】:Orphan removal not working if not "complete" orphan如果不是“完成”孤儿,孤儿删除不起作用
【发布时间】:2015-12-16 17:53:02
【问题描述】:

我有

public class RelationObject  {
    @OneToMany(orphanRemoval = true, mappedBy = "relationObject")
    private Set<RelationParticipant> participants = new HashSet<RelationParticipant>();
}

public class BusinessObject  {
    @OneToMany(orphanRemoval = true, mappedBy = "businessObject")
    private Set<RelationParticipant> participants = new HashSet<RelationParticipant>();
}

public class RelationParticipant {
    @ManyToOne
    @JoinColumn(name = "ro_id", nullable = false)
    private RelationObject relationObject;

    @ManyToOne
    @JoinColumn(name = "bo_id", nullable = false)
    private BusinessObject businessObject;
}

我有一个 RelationParticipant 连接到一个 RelationObject (relobj) 和一个 BusinessObject。 现在我执行 em.remove(relobj),在提交或刷新时我得到一个完整性异常。或者有时我不会,这取决于。

根据 JPA 规范,“如果将删除操作应用于托管源实体,则删除操作将级联到关系目标”。但有时这不会发生。有时会。为什么?

【问题讨论】:

    标签: hibernate jpa jpa-2.0


    【解决方案1】:

    JPA 声明孤立删除的操作与关系上的级联删除相同,因此无需设置级联删除 - 当您删除 RelationObject 时,如果 orphanRemoval = true,JPA 将删除所有引用的 RelationParticipants。

    您可能不会在所有情况下都看到这种行为,因为 JPA 只能将删除级联到它知道的实体。在您的情况下,检查 RelationObject 的参与者集合是否真的包含 2 个在数据库中引用它的 RelationParticipants。如果没有,JPA 只会删除它知道的那些。一种快速的检查方法是在 RelationObject 上调用 remove 之前对其调用 refresh。这在不维护双向关系双方的应用程序中很常见,例如,通过设置 RelationParticipants->RelationObject 引用而不将 RelationParticipants 添加到 RelationObject 的参与者集合中。如果是这种情况,JPA 不会为您维护双向关系 - 应用程序需要保持双方同步。

    【讨论】:

    • 谢谢,但您能否指出 JPA 规范在哪里读作“当您删除 RelationObject 时,如果 orphanRemoval = true,JPA 将删除任何引用的 RelationParticipants”?我咨询的来源(不是 JPA,虽然)并不能保证这一点。它们保证仅对断开连接的实体进行级联,例如我执行 relobj.getParticipants().clear(),或者更确切地说是 RelationParticipant.setRelationObject(null)。或者你基本上是说 em.remove(relobj) 也算作断开其参与者?
    • 等等,我自己发现了:“如果将remove操作应用于托管源实体,remove操作将按照3.2.3节的规则级联到关系目标, (因此没有必要为关系指定 cascade=REMOVE)"
    • 在删除之前,集合应该没问题。我刚刚通过查询获取了 relobj,因此它与参与者的链接(和反向链接)由 JPA 提供者维护。我刚才检查了一下,有 2 个链接指向不同的参与者,每个参与者都有一个指向 relobj 的链接(同一个实例)。调用刷新没有任何改变,当 orphanRemoval = true 时仍然会出现异常,除非事先删除了至少一个参与者。
    • 打开 SQL 日志记录,看看语句顺序发生了哪些变化,以及究竟是什么导致了约束违规。
    【解决方案2】:

    经过几天的研究,我发现了以下行为。 OrphanRemoval 仅将删除级联到成为孤儿的实体;不仅是一个关系的孤儿(其源被删除),而且是所有关系的孤儿。示例:如果我删除其 Target[] 中包含 X 的 Source,则 X 将成为孤立的,除非存在另一个 X 也是 Target 的源 S。当然,当 X 是 S 的“主人”(S 包含对 X 的 FK)时,这是很自然的;但是即使 X 是 S 的“孩子”(X 包含到 S 的 FK,并且 X 是反向关系的源:“mappedBy=...”),级联也不会发生。 我在 JPA 规范中找不到这个特殊条件,所以也许我错了?但至少这个解释是有道理的。

    因此,在我的示例中,删除 relobj 不会级联到其 RelationParticipants,因为后者中的每一个也由其 BusinessObject 引用(作为关系的反向、非拥有方)。

    另一个有趣的行为:即使在这种情况下,级联仍然可以发生,如果 EntityManager 不知道任何障碍。比如说,在 DB 中有一个引用 RelationParticipant 的 BusinessObject,但它还没有被 EntityManager 加载。在这种情况下,EntityManager 将不会检测到此 BusinessObject 对 RelationParticipant 的反向引用,并且 em.remove(relobj) 将成功级联到 relobj.getParticipants()。

    【讨论】:

    • stackoverflow.com/a/34843369/4101144 可能正在描述类似的情况并给出详尽的答案。除其他外,它解释了 Hibernate 在刷新 DB 时如何遍历所有引用,因此如果引用包含 MERGE 级联,它可以取消通过另一个引用由孤儿删除计划的删除。不过,我应该注意,在我的情况下,我没有在日志中收到任何“未安排”消息!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-23
    • 2011-01-02
    • 2012-10-22
    • 2012-02-07
    • 2011-02-09
    • 2014-10-07
    相关资源
    最近更新 更多