为什么 JPA 在 merge() 时执行双重插入
在 EclipseLink 中,我遇到一个问题,一个元素被插入两次,导致主键冲突。场景如下: 我有三个实体:Element、Restriction 和 RestrictionElement。实体 RestrictionElement 充当其他两个实体之间的多对多关系。 当我创建新的 RestrictionElement 并合并该元素时,RestrictionElement 被插入两次。代码:
// element is an Element, restriction is a Restriction. Both are already in present in the database.
RestrictionElement newRestrictionElement = new RestrictionElement(restriction, element);
Transaction transaction = new Transaction();
em.merge(element); //em is the EntityManager
transaction.commit();
但是,如果我删除行 restriction.getReferencedRestrictionElements().add(this);
RestrictionElement 将插入一次。 谁能解释为什么会发生这种情况?或者指向一个文档来解释如何计算 merge() 命令的作用?
相关JPA代码:(我只给出一小部分。代码没有任何其他大问题。)
public class RestrictionElement {
@JoinColumns({@JoinColumn(name = "ELEMENT_ID", referencedColumnName = "ID"),@JoinColumn(name = "ELEMENT_DESCRIPTOR", referencedColumnName = "DESCRIPTOR")})
private Element element;
@JoinColumns({@JoinColumn(name = "RESTRICTION_ID", referencedColumnName = "ID"),@JoinColumn(name = "RESTRICTION_DESCRIPTOR", referencedColumnName = "DESCRIPTOR")})
private Restriction restriction;
public RestrictionElement(Restriction restriction, Element element) {
this.restriction = restriction;
this.element = element;
restriction.getReferencedRestrictionElements().add(this);
element.getReferingRestrictionElements().add(this);
}
}
public class Element {
@OneToMany(mappedBy = "element")
private List<RestrictionElement> referingRestrictionElements = new ArrayList<RestrictionElement>();
}
public class Restriction extends Element {
@OneToMany(mappedBy = "restriction", cascade = { ALL, PERSIST, MERGE, REMOVE, REFRESH })
private List<RestrictionElement> referencedRestrictionElements = new ArrayList<RestrictionElement>();
}
In EclipseLink, I run into a problem where an element is inserted twice, resulting into a primary key violation. The scenario is as follows:
I have three entities, Element, Restriction and RestrictionElement. The entity RestrictionElement acts as a many-to-many relationship between the two others.
When I create a new RestrictionElement and merge the Element, the RestrictionElement is inserted twice. The code:
// element is an Element, restriction is a Restriction. Both are already in present in the database.
RestrictionElement newRestrictionElement = new RestrictionElement(restriction, element);
Transaction transaction = new Transaction();
em.merge(element); //em is the EntityManager
transaction.commit();
However, if I remove the line restriction.getReferencedRestrictionElements().add(this);
the RestrictionElement is inserted once.
Can anyone explain why this happens? Or point to a document that explains how to work out what the merge() command does?
Relevant JPA code: (I'll only given a small part. There aren't any other big problems with the code.)
public class RestrictionElement {
@JoinColumns({@JoinColumn(name = "ELEMENT_ID", referencedColumnName = "ID"),@JoinColumn(name = "ELEMENT_DESCRIPTOR", referencedColumnName = "DESCRIPTOR")})
private Element element;
@JoinColumns({@JoinColumn(name = "RESTRICTION_ID", referencedColumnName = "ID"),@JoinColumn(name = "RESTRICTION_DESCRIPTOR", referencedColumnName = "DESCRIPTOR")})
private Restriction restriction;
public RestrictionElement(Restriction restriction, Element element) {
this.restriction = restriction;
this.element = element;
restriction.getReferencedRestrictionElements().add(this);
element.getReferingRestrictionElements().add(this);
}
}
public class Element {
@OneToMany(mappedBy = "element")
private List<RestrictionElement> referingRestrictionElements = new ArrayList<RestrictionElement>();
}
public class Restriction extends Element {
@OneToMany(mappedBy = "restriction", cascade = { ALL, PERSIST, MERGE, REMOVE, REFRESH })
private List<RestrictionElement> referencedRestrictionElements = new ArrayList<RestrictionElement>();
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
不要忘记,一旦使用 JPA 检索该类的实例,该实例就会成为托管的,对其进行的任何更改都将自动合并到数据库中。
默认情况下,此合并将在您查询表时发生。因此可能会出现以下情况:
类似上述情况,第二次查找将有效地向第一个表发出合并。 (我不确定这里的细节或场景)。
我的建议是,在开始修改(即设置等)之前,您先发出每个查询(例如 findById)或您拥有的每个实例。
希望有帮助。
Don't forget that once you retrieve an instance of the class using JPA, the instance becomes managed, any changes to it will be automatically merged into the database.
By default, this merge will occur at the moment you query the table. Therefore the following situation can happen:
in a situation similar to the above, the second find will effectively issue a merge to the first table. (I'm not sure exactly of the details or scenarios here).
My suggestion is that you issue every single query (findById for example) or every instance you have before you start modifying it (ie, set, etc).
Hope it helps.
我在运行程序时遇到了类似的问题,但在逐步调试下问题不存在。
我通过将 OneToMany 关系中的 List 更改为 Set 解决了该问题。
I got a similar issue when I run my program, but the issue is not there under step by step debugging.
I resolved the issue by changing List to Set in the OneToMany relationship.
你如何持久化RestrictionElement?我的猜测是,当你坚持它时,你会得到一个副本,然后当你将元素与对其的引用合并时,你会得到第二个副本。
尝试对新对象使用 persist() ,并在使用正确的托管副本管理对象后关联这些对象。
How do your persist RestrictionElement? My guess is when you persist it you get one copy, then a second when you merge the Element with the reference to it.
Try using persist() for new objects, and related the objects after they are managed with the correct managed copy.