为什么 JPA 在 merge() 时执行双重插入

发布于 2024-11-08 06:15:45 字数 1907 浏览 0 评论 0原文

在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

百善笑为先 2024-11-15 06:15:50

不要忘记,一旦使用 JPA 检索该类的实例,该实例就会成为托管的,对其进行的任何更改都将自动合并到数据库中。

默认情况下,此合并将在您查询表时发生。因此可能会出现以下情况:

  • 查询(通过ID查找)
  • 更新(setName =“xx”)
  • 查询与此有直接关系的另一个类(再次通过ID查找)

类似上述情况,第二次查找将有效地向第一个表发出合并。 (我不确定这里的细节或场景)。

我的建议是,在开始修改(即设置等)之前,您先发出每个查询(例如 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:

  • query (find by ID)
  • update (setName = "xx")
  • query another class that has a direct relationship to this one (find by ID again)

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.

猫烠⑼条掵仅有一顆心 2024-11-15 06:15:49

我在运行程序时遇到了类似的问题,但在逐步调试下问题不存在。

我通过将 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.

就是爱搞怪 2024-11-15 06:15:48

你如何持久化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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文