开发者

Why does JPA do a double insert upon merge()

开发者 https://www.devze.com 2023-03-06 15:36 出处:网络
In EclipseLink, I run into a problem where an element is inserted twice, resulting into a primary key violation. The scenario is as follows:

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 o开发者_StackOverflow中文版ut 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>();
}


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.


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消