2016-12-14 98 views
0

我正在處理需要級聯創建和通過大型JPA註釋實體合併的WebSphere 8.5.5(OpenJPA 2.2.3)中的項目模型。在通過調用Grand-Parent上的EntityManager.merge()或者在事務提交時觸發flush來合併grand-children時,我們遇到了一個非常具體的問題。下面是詳細信息:插入JPA實體與複合EmbeddedId(包含另一個EmbeddedId的EmbeddedId)的順序

實體映射的

相關部分:

  1. EntityA具有一對多到EntityB
  2. EntityB具有一對多到EntityC
  3. EntityC具有一對多到EntityD

全部都有雙向映射。實體A和B具有單列主鍵。實體C具有組合主鍵,其包括實體B的主鍵的外鍵。實體D具有包含實體C的組合鍵的組合鍵。請參見下面的映射。

@Entity 
@Table(name="TableA") 
public class EntityA extends BaseEntity { 

    @Id 
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="TABLE_A_ID_GEN") 
    @SequenceGenerator(name="TABLE_A_ID_GEN", sequenceName="TABLE_A_ID", allocationSize=1) 
    @Column(name="TABLE_A_ID") 
    private Integer id; 

    @OneToMany(fetch=FetchType.LAZY, mappedBy="entityA", cascade=CascadeType.ALL) 
    private List<EntityB> entityBList; 

    ... 

} 

@Entity 
@Table(name="TableB") 
public class EntityB extends BaseEntity { 

    @Id 
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="TABLE_B_ID_GEN") 
    @SequenceGenerator(name="TABLE_B_ID_GEN", sequenceName="TABLE_B_ID", allocationSize=1) 
    @Column(name="TABLE_B_ID") 
    private Integer id; 

    @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL) 
    @JoinColumn(name="TABLE_A_ID") 
    private EntityA entityA; 

    @OneToMany(fetch=FetchType.LAZY, mappedBy="entityB", cascade=CascadeType.ALL) 
    private List<EntityC> entityCList; 

    ... 

} 

@Entity 
@Table(name="TableC") 
public class EntityC extends BaseEntity { 

    @EmbeddedId 
    private EntityC_PK id = new EntityC_PK(); 

    @MapsId("entityB_Id") 
    @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL) 
    @JoinColumn(name="TABLE_B_ID") 
    private EntityB entityB; 

    @OneToMany(fetch=FetchType.LAZY, mappedBy="entityC", cascade=CascadeType.ALL) 
    private List<EntityD> entityDList; 

    ... 

} 

@Embeddable 
public class EntityC_PK implements BaseComponent { 

    @Column(name="TABLE_B_ID", nullable = false, updatable = false) 
    private Integer entityB_Id; 

    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="TABLE_C_ID_GEN") 
    @SequenceGenerator(name="TABLE_C_ID_GEN", sequenceName="TABLE_C_ID", allocationSize=1) 
    @Column(name="TABLE_C_ID") 
    private Integer entityC_Id; 

    ... 

} 

@Entity 
@Table(name="TABLE_D") 
public class EntityD extends BaseEntity { 

    @EmbeddedId 
    private EntityD_PK id = new EntityD_PK(); 

    @MapsId("entityC_Id") 
    @JoinColumns({ 
     @JoinColumn(name = "TABLE_B_ID"), 
     @JoinColumn(name = "TABLE_C_ID")}) 
    @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL) 
    private EntityC entityC; 

    ... 

} 

@Embeddable 
public class EntityD_PK implements BaseComponent { 

    @Embedded 
    private EntityC_PK entityC_Id; 

    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="TABLE_D_ID_GEN") 
    @SequenceGenerator(name="TABLE_D_ID_GEN", sequenceName="TABLE_D_ID", allocationSize=1) 
    @Column(name="TABLE_D_ID") 
    private Integer entity_id; 

    ... 

} 

什麼工作:

您可以撥打實體A的EntityManager.persist()(附帶的所有兒童)和模型將級聯正確堅持。

什麼行不通:

如果實例實體A和呼叫EntityManager.persist(entityA),然後加入孩子,大兒等,當你EntityManager.merge(entityA)(或允許提交事務時的隱式合併)將無法按正確的順序執行INSERT語句。爲了讓事情更加混亂,INSERTS的順序在重複執行單元測試時是不一致的。它未能通過嘗試實體C.

之前插入實體d的問題:

如何,我們糾正JPA註解,在合併執行正確的插入順序(和更新/刪除)?

編輯1: 插入/刪除順序至關重要,因爲數據庫強制外鍵關係與約束。

+0

在這裏看到我的答案,這可能是有用的:http://stackoverflow.com/questions/39024310/openjpa-nested-onetomany-relationships-merge-issue/39025865#39025865 –

回答

2

讓我先說明(也許我在說明顯,對不起)您應該查看您的場景的JPA規範.......嵌入式有時對它們有不同的規則。接下來,你聲明'EntityManager.create()',但我認爲你的意思是.persist?你後來談論合併,所以也許你的意思是.merge?無論哪種方式,我建議你堅持.persist如果你想堅持新的實體,而不是合併。雖然這不是非法的,但合併通常用於合併分離的實體等。

這樣一來,讓我瞭解一下您的問題的核心,並給您一個可能有助於您的訂單的財產。如果您的ddl包含外鍵約束,則您沒有在文本中聲明。既然你關心的是秩序,我會認爲你有這樣的約束。如果你這樣做,OpenJPA對這個約束一無所知,因此,不知道如何適當地排列事物。默認情況下,您不能依賴於SQL的順序,並且排序的隨機性正是我期望的。但是,如果您需要以支持FK約束的方式進行排序,那麼您需要允許OpenJPA「瞭解」約束條件。爲此,您需要在持久性中設置此屬性。XML文件(或者你可以將其設置爲JVM自定義屬性):

<property name="openjpa.jdbc.SchemaFactory" value="native(ForeignKeys=true)"/> 

此屬性允許OpenJPA的檢查您的架構,並在這樣做時,可以瞭解您的FK約束。有了這些知識,OpenJPA就可以正確地命令SQL。

最後,如果你沒有一個FK約束,但你要訂購的SQL以一定的方式,那麼你可能需要使用此:

<property name="openjpa.jdbc.UpdateManager" value="operation-order"/> 

不要,我重複做不要同時使用這兩個屬性。它可能有奇怪的副作用。請首先關注SchemaFactory屬性,然後如果它不幫助嘗試UpdateManager。操作順序告訴OpenJPA根據持久化實體的方式或換言之操作順序來排序SQL。這可能實際上不會對你的情況有太大​​幫助,因爲你堅持A並期望其他所有事情能夠級聯(OpenJPA可能會持續A,但是對於B和C來說,這是一個先發制人的問題)。但是,如果您堅持A,然後C,然後B,SQL應按照插入A,C,然後將B設置爲「operation-order」的順序進行。

+0

謝謝希思你的有用答案。我發現更多與您的解決方案完全一致的信息。 [鏈接](http://openjpa.208410.n2.nabble.com/Inconsistent-execution-order-of-INSERT-statements-using-cascading-persist-td678745.html)。您沒有提到的一個選項是使用OpenJPA的@ForeignKey註釋來標記哪些關係具有外鍵並需要特定的插入順序。 –

+1

謝謝!我故意把它留下(也許我不應該)。 OpenJPA的'@ForeignKey'有兩個問題:1)它是合適的。 2)它把開發者的責任放在所有正確的地方(錯過一個地方,所有投注都關閉)。在你的情況下,幾個班級沒什麼大不了的,但是在一個大型的應用程序中,它讓事情變得更加困難。我不會說不使用'@FK',但我會認真考慮使用一行屬性(無代碼更改),而不是將'@FK'添加到域模型中的正確位置。祝你好運,享受! –