How to delete children on update parent?
我有"父母"和"孩子"hibernate实体。
在"父母"上,我有一个
当我用新的子代更新父代时,一切正常:子代在" child "表上创建。
但是,当我从父哈希集中删除一个元素并保存时,不会删除数据库中的对应子对象。
这里是:
在父项上(命名为工作流程):
1 2 | @OneToMany(orphanRemoval=true, cascade = CascadeType.ALL, mappedBy="workflow", fetch = FetchType.EAGER) private Set<ActivityDB> activities; |
在子上(命名为活动)
1 2 3 4 | @ManyToOne @JoinColumn(name="id_workflow") @Fetch(FetchMode.JOIN) private WorkflowDB workflow; |
我正在处理会话内的持久实例。没有错误。似乎工作正常,但数据库上的注册仍然存在。
要进行测试,请加载工作流程并执行
1 | workflow.activities.remove( activity_index_x ) |
,然后使用
但是" activity_index_x "仍在数据库中,当我重新加载工作流时,它又恢复了生命。
确保您已阅读有关双向关联链接的手册。
最佳实践包括添加添加/删除子方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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和hashcode。最好的方法是使用业务密钥来检查相等性和哈希码算法,而永远不要将数据库标识符用于equals / hashcode,尤其是与类似哈希的数据结构(set / map)结合使用时。
双向关联的管理比单向关联更复杂。如果您确实不需要一对多的一面,则可以将其删除并用查询代替它。这样,您只需要管理多对一方面。
这是由于未清除子级对父级引用。由于您已映射了双方(并以此方式进行了配置),因此Hibernate实际上将查看该关系的子端。
解决此问题的最佳方法是,当您从工作流程中删除活动时(以及相反),还要清除活动中的工作流程字段,因此:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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); } } } |
主要问题是您要保持关系状态的哪一侧?
您还可以将关系映射到工作流(不使用mapledBy属性,而使用JoinTable批注将列保留在子表上),而仅将父工作流映射为只读(insertable = false,updatable = false)字段。
这样,工作流完全可以控制哪些活动是其中的一部分,并且这些活动仍可以看到它们所属的工作流。
1 2 3 4 5 6 7 8 9 10 11 | class Workflow { @OneToMany @JoinTable(...) private Set<Activity> activities } class Activity { @Column(insertable=false, updatable=false) private Workflow workflow } |