java 一对多关系 JPA/Hibernate 删除链接
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5641690/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
One to Many relationship JPA/Hibernate removing links
提问by hello
I have bidirectional relationship setup as follows:
我的双向关系设置如下:
class Child{
@ManyToOne
@JoinTable(name = "CHILDREN_WITH_PARENT",
joinColumns = {@JoinColumn(name = "CHILD_ID")},
inverseJoinColumns = {@JoinColumn(name = "PARENT_ID")}
)
private Parent parent;
}
class Parent{
@OneToMany(mappedBy="parent", cascade=CascadeType.ALL)
Set<Child> childrens = new HashSet<Child>();
public void persistOrMerge() {
EntityManager em = entityManager();
em.getTransaction().begin();
try {
if (em.contains(this))
return;
if (id == null || id == 0) {
this.setCreatedDate(new Date());
em.persist(this);
} else {
Parent prev = em.find(Parent.class, this.id);
if (prev == null) {
em.persist(this);
} else{
this.setCreatedDate(new Date());
em.merge(this);
}
}
em.flush();
em.getTransaction().commit();
} finally {
em.close();
}
}
}
On my client side I have following code (GWT + EntityProxy)
在我的客户端,我有以下代码(GWT + EntityProxy)
Set<ChildProxy> children = new HashSet<ChildProxy>();
if(childIsNew)
child = request.create(Children.class)
else
child = request.edit(oldChild)
children.add(child);
//If children are deleted, they are not contained in the set
//we are sending back to server
parent.setChildren(children)
parent.persistOrMerge();
This code only works for adding new children. Removing of children from parent does not work even if parent class receives an empty children set. The linkages in JOIN table are not removed.
此代码仅适用于添加新子项。即使父类收到一个空的子集,从父级中删除子级也不起作用。JOIN 表中的链接不会被删除。
Can you please tell where I am missing something?
你能告诉我哪里遗漏了什么吗?
Thanks!
谢谢!
回答by Edwin Dalorzo
I will start by saying that is a really bad idea that the entity is the one using the entity manager directly.
我首先要说的是,实体是直接使用实体管理器的实体是一个非常糟糕的主意。
The EntityManager.merge() method returns the actual merged instance, this implies that in your code, when you issue
EntityManager.merge() 方法返回实际合并的实例,这意味着在您的代码中,当您发出
em.merge(this)
You have no guarantee whatsoever that the merged instance corresponds to "this" anymore, and from that point on you may see all kinds of logical problems.
您无法保证合并后的实例再对应于“this”,从那时起您可能会看到各种逻辑问题。
If you do not think this is such a big deal, your problem should be solved by turning on orphan removal on the OneToMany side of the relationship, provided that the children are not being used anywhere else in other relationships. Otherwise you will have to do the merging manually.
如果您认为这不是什么大问题,那么您的问题应该通过在关系的 OneToMany 端打开孤儿移除来解决,前提是孩子没有在其他关系中的任何其他地方使用。否则,您将不得不手动进行合并。
@OneToMany(mappedBy="parent", cascade=CascadeType.ALL, orphanRemoval=true)
Set<Child> childrens = new HashSet<Child>();
The JPA 2.0 specification states that
JPA 2.0 规范指出
Associations that are specified as OneToOne or OneToMany support use of the orphanRemoval option. The following behaviors apply when orphanRemoval is in effect:
指定为 OneToOne 或 OneToMany 的关联支持使用 orphanRemoval 选项。当 orphanRemoval 生效时,以下行为适用:
- If an entity that is the target of the relationship is removed from the relationship (by setting the relationship to null or removing the entity from the relationship collection), the remove operation will be applied to the entity being orphaned. The remove operation is applied at the time of the flush operation. The orphanRemoval functionality is intended for entities that are privately "owned" by their parent entity. Portable applications must otherwise not depend upon a specific order of removal, and must not reassign an entity that has been orphaned to another relationship or otherwise attempt to persist it. If the entity being orphaned is a detached, new,or removed entity, the semantics of orphanRemoval do not apply.
- If the remove operation is applied to a managed source entity, the remove operation will be cascaded to the relationship target in accordance with the rules of section 3.2.3, (and hence it is not necessary to specify cascade=REMOVE for the relationship)[20].
- 如果作为关系目标的实体从关系中移除(通过将关系设置为 null 或从关系集合中移除实体),移除操作将应用于被孤立的实体。在刷新操作时应用删除操作。orphanRemoval 功能适用于由其父实体私有“拥有”的实体。便携式应用程序不得以其他方式依赖于特定的删除顺序,并且不得将已孤立的实体重新分配给另一个关系或以其他方式尝试保留它。如果被孤立的实体是分离的、新的或移除的实体,则 orphanRemoval 的语义不适用。
- 如果删除操作应用于受管源实体,则删除操作将根据第 3.2.3 节的规则级联到关系目标,(因此不必为关系指定级联=REMOVE)[ 20]。
回答by Mallik
// In the entity class of Parent1 write a method to unlink parent2
public void unLinkParent2(Parent2 parent2)
{
//remove columns from the link table, pertaining to the parent2
getParent2Collection().remove(parent2);
// using the parent2 object remove 'this' parent1 entity link columns
parent2.getParent1Collection().remove(this);
}
Parent1: Parent2: LinkP1-P2 -------------------------------------------------- Id1(PK) Id2(PK) Id1 (composite PK) name1 Name2 Id2 (composite PK)
Id1
and Id2
of the Link table together is a primary key referring to Parent1
and Parent2
tables.
Id1
和Id2
链接表一起是主键引用Parent1
和Parent2
表。
回答by Chris
The ManyToOne mapping controls the relationship - you have marked the OneToMany as mappedby the child's relation to its parent. This means that changes to the relationship are only picked up if merged from the child side. Because children removed from the parents list are no longer in it, merge cannot be cascaded to them, and any changes in them are never persisted to the database.
ManyToOne 映射控制关系 - 您已将 OneToMany 标记为映射的子级与其父级的关系。这意味着只有在从子端合并时才会接收对关系的更改。因为从父列表中删除的子项不再在其中,因此无法将合并级联到它们,并且它们中的任何更改都不会持久化到数据库中。
As others pointed out, orphan removal will allow for any children not in the list to be deleted on merge. This might be overkill when all you want is the relationship nulled out. In this scar, you can probably reverse the relationship so that it is owned by the parent. That will ensure the relation gets updated, but any changes in removed children will still not get picked up. The only way to get those changes picked up is to add the children to a new parent that gets merged, or to merge them independently. If they can exist without a parent, calling persist/merge on them directly is the better option.
正如其他人指出的那样,孤儿删除将允许在合并时删除不在列表中的任何孩子。当您想要的只是消除关系时,这可能是矫枉过正。在这个疤痕中,您可能可以扭转关系,使其归父母所有。这将确保关系得到更新,但移除的孩子的任何变化仍然不会被接受。获取这些更改的唯一方法是将子项添加到合并的新父项中,或者单独合并它们。如果它们可以在没有父项的情况下存在,直接对它们调用 persist/merge 是更好的选择。
回答by atamanroman
This question is related: JPA CascadeType.ALL does not delete orphans