2011-12-16 100 views
1

我在單個事務中遇到多個EntityManager.merge()調用的問題。這是使用Oracle數據庫。這兩個對象都不存在。實體:多個Eclipselink需要Flush()在同一個事務中合併?

public class A { 
    @Id 
    @Column("ID") 
    public Long getID(); 

    @OneToOne(targetEntity = B.class) 
    @JoinColumn("ID") 
    public B getB(); 
} 

public class B { 
    @Id 
    @Column("ID") 
    public Long getID(); 
} 

合併代碼看起來是這樣的:

@Transactional 
public void create(Object A, Object B) { 
    Object A = entitymanager.merge(A); 
    B.setId(A.getId()); 
    entitymanager.merge(B); 
} 

對象是通過序列生成的ID,並大幹快上B.在日誌中尋找正確設置,合併對A被稱爲在合併之前調用B。從A到B有一個@OneToOne映射。但是,在實際提交時,在方法結束時,它會嘗試在B上執行INSERT,然後執行A上的INSERT操作,該操作會引發IntegrityConstraintViolation,因爲「父鍵未找到」。

如果我在第二次合併之前添加entitymanager.flush(),它工作正常。

@Transactional 
public void create(Object A, Object B) { 
    Object A = entitymanager.merge(A); 
    entitymanager.flush(); 
    B.setId(A.getId()); 
    entitymanager.merge(B); 
} 

但是,flush()是一個昂貴的操作,不應該是必需的。所有這些應該發生在同一個交易中(默認傳播@TransactionalPropagation.REQUIRED)。

任何想法爲什麼這不起作用,沒有flush(),以及爲什麼即使合併A發生在B上的合併之前,實際上在COMMIT上的INSERT被顛倒了?

回答

0

如果實體A和B沒有關係(即@OneToOne,@OneToMany,...),則持久性提供程序無法計算正確的插入順序。 IIRC EclipseLink在將SQL語句發送到數據庫時不使用對象創建順序。

如果您想避免使用flush(),只需將您的約束設置爲延遲。

+0

據我所知,A和B有一個「@ OneToOne」關係,下面的方法在對象A上: `@OneToOne(fetch = FetchType.EAGER,cascade = CascadeType.REMOVE,optional = true, targetEntity = B.類) public B getB(){}` – Trisfall 2011-12-19 13:25:57

+0

通過「設置你的約束被推遲」,你是否引用數據庫中沒有外鍵約束?這似乎也不是一個好的選擇。我不得不認爲必須有一種方法來強制執行Eclipselink中的INSERT語句的順序。 – Trisfall 2011-12-19 13:58:33

0

正如弗蘭克所說,您所顯示的代碼並未設置A-> B關係,因此提供者無法知道此B對象需要在A之前插入。其他關係可能會導致它認爲總體上需要先插入A.

推遲約束可以在一些數據庫上完成,並且指的是設置數據庫以推遲約束處理直到事務結束。如果您推遲或刪除約束,則可以查看正在生成的SQL是否正確,或者是否存在錯過代碼和映射的其他問題。

0

除非有雙向@OneToOne批註,否則似乎合併是按字母順序排列的(至少,這是一種可能性)。

以前:

public class A { 
    @OneToOne(targetEntity = B.class) 
    @JoinColumn("ID") 
    public B getB(); 
} 

public class B { 
    @Id 
    @Column("ID") 
    public Long getID(); 
} 

現在:

public class A { 
    @OneToOne(targetEntity = B.class) 
    @JoinColumn("ID") 
    public B getB(); 
} 

public class B { 
    @Id 
    @Column("ID") 
    public Long getID(); 

    @OneToOne(targetEntity = A.class) 
    @JoinColumn("ID") 
    public A getA(); 
} 

對於我在做什麼也不要緊,B的方式來獲得,但我還是不明白爲什麼A中的註釋是不夠的。

相關問題