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

时间:2011-05-18 15:49:17

标签: jpa merge eclipselink

在EclipseLink中,我遇到了一个问题,即元素被插入两次,导致主键违规。方案如下: 我有三个实体,元素,限制和限制元素。实体RestrictionElement充当两个其他实体之间的多对多关系。 当我创建一个新的RestrictionElement并合并Element时,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>();
}

3 个答案:

答案 0 :(得分:2)

你如何坚持RestrictionElement?我的猜测是,当你坚持它时,你会得到一个副本,然后在你将Element与它的引用合并时的第二个。

尝试对新对象使用persist(),并在使用正确的托管副本管理对象后将其关联起来。

答案 1 :(得分:1)

不要忘记,一旦使用JPA检索类的实例,实例就会被管理,对它的任何更改都将自动合并到数据库中。

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

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

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

我的建议是你在开始修改它之前发出每一个查询(例如findById)或每个实例(即set等)。

希望它有所帮助。

答案 2 :(得分:1)

我运行程序时遇到了类似的问题,但问题是一步一步调试。

我通过在OneToMany关系中将List更改为Set来解决了这个问题。