2011-05-18 60 views
0

在EclipseLink中,我遇到了一個插入元素兩次的問題,導致主鍵違規。場景如下: 我有三個實體,Element,Restriction和RestrictionElement。實體RestrictionElement充當另外兩個之間的多對多關係。 當我創建一個新的RestrictionElement併合並元素時,RestrictionElement被插入兩次。代碼:爲什麼JPA在合併時執行雙重插入()

// 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>(); 
} 
+0

在您的RestrictionElement中的代碼中,我看不到您聲明瞭關係的性質(即@OneToOne,@ManyToOne等)。我在這裏錯過了什麼嗎? – 2011-05-18 16:56:34

+0

真實的代碼並不包含它,並且大多數情況下,它的工作原理很明顯並不是必需的。 – bspoel 2011-05-20 10:04:12

+0

這不是一個趣味問題,這是[JPA規範](http://www.jcp.org/en/jsr/detail?id=317)的一部分。此外,在這些註釋中,您可以定義給定關聯的合併行爲。您甚至可以將OneToMany映射到二元關聯另一側的不存在的關係。如果我是你,我會先解決這個問題,然後看看問題是否存在,但在實體沒有正確註釋之前,沒有人能確定事情爲什麼沒有按預期行事。這裏的問題是爲什麼你的代碼不起作用,所以你不應該低估這一點。 – 2011-05-20 15:00:44

回答

2

如何堅持你RestrictionElement?我的猜測是,當你堅持它你得到一個副本,然後當你合併元素與它的引用時,第二個。

嘗試對新對象使用persist(),並在用正確的託管副本管理對象後嘗試使用它們。

+0

我沒有明確堅持RestrictionElement。不幸的是,堅持新的對象需要大量的重構,這是我們不願意在項目的這個階段採取的一步(它很快就要退役了......) – bspoel 2011-05-20 10:02:44

1

不要忘記,一旦您使用JPA檢索類的實例,實例就變爲託管,任何對它的更改都會自動合併到數據庫中。

默認情況下,此合併將在您查詢表的那一刻發生。因此,下面的情況可能發生:

  • 查詢(通過ID查找)
  • 更新(的setName =「XX」)
  • 查詢有這一個直接關係的其他類(以ID再次找到)

在類似於上面的情況下,第二次查找將有效地發出合併到第一個表。 (我不確定這裏的細節或場景)。

我的建議是,在開始修改它(即設置等)之前,您發出每個單個查詢(例如findById)或每個實例。

希望它有幫助。

1

當我運行我的程序時,我遇到了類似的問題,但問題不在逐步調試下。

我解決了這個問題,通過更改列表來設置OneToMany關係。