【问题标题】:How to delete children on update parent?如何在更新父级时删除子级?
【发布时间】:2014-09-02 12:08:44
【问题描述】:

我有“父”和“子”休眠实体。

在“父母”上,我有一个 Set<Child> 来容纳它的孩子。

当我用新的子级更新父级时,一切正常:子级是在“子”表上创建的。

但是,当我从 Parent hashset 中删除一个元素并保存时,不会删除数据库上对应的子元素。

这里是:

在 PARENT(命名工作流)上:

@OneToMany(orphanRemoval=true, cascade = CascadeType.ALL, mappedBy="workflow", fetch = FetchType.EAGER)
private Set<ActivityDB> activities; 

On Child(命名活动)

@ManyToOne
@JoinColumn(name="id_workflow")
@Fetch(FetchMode.JOIN)
private WorkflowDB workflow;

我正在会话中处理持久实例。不会引发错误。似乎工作正常,但数据库上的寄存器仍然存在。

为了做一个测试,我加载了工作流并做一个

workflow.activities.remove( activity_index_x )

然后使用session.update( workflow ) 保存工作流。 但“activity_index_x”仍在数据库中,当我重新加载工作流时又恢复了活力。

【问题讨论】:

    标签: java hibernate jpa orm hibernate-mapping


    【解决方案1】:

    请务必阅读有关 bidirectional association links 的手册。

    最佳实践包括添加添加/删除子方法:

    class WorkflowDB {
    
        public void remove (ActivityDB a) {
            if (a != null) {
                this.activities.remove(a);
                a.setWorkflow(null);
            }
        }
    
        public void add (ActivityDB a) {
            if (a != null) {
                this.activities.add(a);    
                a.setWorkflow(this);
            }
        }
    
    }
    

    但是因为你使用 Set 作为一对多的一面,所以你需要特别注意equals and hashcode。最好的方法是使用业务密钥来检查相等性和哈希码算法,并且永远不要将数据库标识符用于equals/hashcode,尤其是与类似哈希的数据结构(set/map)结合使用时。

    双向关联比单向关联更难管理。如果您真的不需要一对多的一面,您可以将其删除并用查询替换它。这样你就只需要管理多对一的一方。

    【讨论】:

    • 实际上,我正在 WorkflowDB.activities 上设置一个全新的活动列表。我以前不知道会添加或删除什么活动。所有这些都来自 GUI,可以添加或删除活动,因此我需要在持久实例和活动的瞬态中搜索,以了解必须添加、保留或删除的内容。我创建了一个新 List 并执行此操作:仅临时列表上的活动是新的,因此我复制到新列表。必须保留两个列表上的活动,因此我将持久版本复制到新列表并在工作流中设置新列表。行为。只有在持久列表上必须被删除。
    • 这种策略很常见,尤其是当您在删除旧行/插入新行时可能违反的唯一约束时,它们都在相同的约束要求下。双向关联比单向关联更难管理。如果您真的不需要一对多的一面,您可以将其删除并用查询替换它。这样你就只需要管理多对一的一方。
    • 好吧,经过数小时的痛苦,我决定创建一个存储库来管理活动。我得到了更多的工作,但更少的痛苦,现在一切都很好。不管怎么说,还是要谢谢你。我会奖励你非常详细的回答(16 小时后,S.O. 说......)。
    • 所以你仍然有双向关联,但是你手动将传入的活动与数据库的活动合并。在我的上一个项目中,我不得不求助于这样的解决方案,所以不用担心。就我而言,是刷新时的休眠操作命令对我不利。
    • 可能使用完全父控制的集合和只读反向字段(如我的回答中所述)对您来说会更好。
    【解决方案2】:

    这是由于子对父引用未清除造成的。由于您映射了双方(并以这种方式配置)Hibernate 实际上会查看关系的子端。

    解决此问题的最佳方法是在您从工作流中删除活动时(反之亦然)清除活动上的工作流字段,因此:

    class Workflow {
    
      public void remove (Activity a) {
        if (this.activities.remove(a)) {
          a.setWorkflow(null);
        }
      }
    
      public void add (Activity a) {
        if (this.activities.add(a)) {
          a.setWorkflow(this);
        }
      }
    }
    

    主要问题是您希望在关系的哪一侧保持关系状态? 您还可以在 Workflow 上映射关系(不要使用 mappedBy 属性,而是使用 JoinTable 注释将列保留在子表上)并且仅将 parent-Workflow 映射为只读(insertable=false,updatable= false) 活动中的字段。 这样,工作流完全可以控制哪些活动是它的一部分,并且活动仍然可以看到它们所属的工作流。

    class Workflow {
    
        @OneToMany
        @JoinTable(...)
        private Set<Activity> activities
    }
    
    class Activity {
        @Column(insertable=false, updatable=false)
        private Workflow workflow
    }
    

    【讨论】:

    • 你和 Vlad Mihalcea 给了我同样连贯的答案。请阅读我对 Vlad Mihalcea 的评论。我会奖励更快的。
    • 谢谢。不幸的是,我只能奖励一个答案,但你也值得。
    猜你喜欢
    • 2013-02-19
    • 1970-01-01
    • 1970-01-01
    • 2013-01-31
    • 1970-01-01
    • 2013-01-19
    • 2021-12-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多