2016-08-21 101 views
0

我想導入具有一組職業的個人檔案和一組職業組。專業分佈在專業組中。休眠:重複的鍵值違反集合上的唯一約束

以下實體的定義:

@Entity 
public class Profile extends BaseEntity<Profile> { // B.E. defines id, creation_time,etc.. 

    @OneToMany(cascade = CascadeType.ALL) 
    private Collection<Profession> professions; 

    @OneToMany(cascade = CascadeType.ALL) 
    private Collection<ProfessionGroup> professionGroups; 

    // .. getters and setters 
} 

@Entity 
private class Profession extends BaseEntity<Profession> { 

    @Column(unique = true) 
    private String name; 

    // getters and setters 

} 

@Entity 
public class ProfessionGroup extends BaseEntity<ProfessionGroup> { 

    @Column(unique = true) 
    private String name; 

    @ManyToOne(cascade = CascadeType.All) 
    private Collection<Profession> professions; 

    // getters and setters 
} 

下面的代碼序列化爲JSON一些配置文件讀取,並希望將其存儲到數據庫中:

// ... 
Profile p = ...; // read from json using some deserializer 
p.getProfessionGroups().forEach(pg -> pg.setProfessions(p.getProfessions()); 

// .. 
ProfileService profileService = ...; // 
profileService.save(profile); 

的ProfileService內部調用entityManager.persist(。 ..)。 這裏的問題是,我得到一個「重複的鍵值違反唯一約束」,每當我想分配所有職業到所有professionGroups。我能做些什麼來安全地存儲配置文件,而不會得到唯一的密鑰約束違規。 JPA顯然希望爲專業組中的每個條目創建一個新的專業。但是,對職業的參考是相同的。調用合併(...)沒有成功。

回答

1

解決方案可有時別的地方找到:

實體「專業」和「professiongroup」之間的關係是錯誤的。它應該是:

@Entity 
public class ProfessionGroup extends BaseEntity<ProfessionGroup> { 

    @Column(unique = true) 
    private String name; 

    @ManyToMany(cascade = CascadeType.All) 
    private Collection<Profession> professions; 

    // getters and setters 
    // .. 
} 

一種職業可以在一個或多個專業組,作爲一個專業組可以有一個或多個行業。有這個固定的,並從@maress適應的寶貴答案現在看起來像這樣的解決方案:

Profile profile = mapper.readValue(json, Profile.class); 

List<Profession> managedProfessions = profile 
     .getProfessions() 
     .stream() 
     .map(p -> { 
      return professionService.update(p); 
     }) 
     .map(Optional::get) 
     .collect(Collectors.toList()); 
profile.setProfessions(managedProfessions); 
profile.getProfessiongroups().forEach(professionGroup -> { 
    professionGroup.setProfessions(managedProfessions); 
}); 

profileService.save(profile); 

小菜一碟。

2

問題在於級聯的定義以及JPA和特別是hibernate如何處理新的實體實例。

當entitymanager.persist被調用時,它存儲和管理實體的狀態,但不是傳遞給EntityManager.persist的實際對象。託管實例和傳遞的參數將有所不同。因此,如果您手動生成ids,則使用同一對象調用entitymanager.persist兩次會導致數據庫中的重複數據庫異常,而不是來自jpa。爲了得到這樣一個圓的這一點,你需要堅持並獲得參照行業情況下,您既可以在配置文件,並在ProfessionGroup使用的管理實體,即:

Profile profile = loadProfiles(); 
List<Profession> managedProfessions = profile 
      .getProfessions() 
      .stream() 
      .map((p) -> entityManager.merge(p)) //Note that we use the returned value, since the returned value is what is actually managed, the passed parameter is not, and will be discarded by the persistent-context 
      .Collect(Collectors.toList()); 
profile.setProfessions(managedProfessions); 
profile.getProfessionGroups().forEach((gr)->gr.setProfessions(managedProfessions)); 

profileService.save(profile); 

有了這個,你可能要刪除Cascade.ALL,只能用Cascade.MERGE```替換它。

+0

你的回答是正確的,但上下文有點廣泛。 – phobos

相關問題