2011-01-21 129 views
10

我很難試圖獲得單向的一對一關係來使用JPA(Provider:Hibernate)。在我看來,這應該不是太麻煩,但顯然JPA /休眠不同意;-)JPA /休眠與共享主鍵的單向一對一映射

問題是,我必須映射一個遺留架構,我不能改變,這個架構使用共享主兩個實體之間的密鑰同時也是一個實體的外鍵。

我創建了一個簡單測試用例:

DB看起來如下:

CREATE TABLE PARENT (PARENT_ID Number primary key, Message varchar2(50)); 

CREATE TABLE CHILD (CHILD_ID Number primary key, Message varchar2(50), 
CONSTRAINT FK_PARENT_ID FOREIGN KEY (CHILD_ID)REFERENCES PARENT (PARENT_ID)); 

CREATE SEQUENCE SEQ_PK_PARENT START WITH 1 INCREMENT BY 1 ORDER; 

父(=之一到一擁有側)看起來如下:

@Entity 
@Table(name = "PARENT") 
public class Parent implements java.io.Serializable {  
    private Long parentId; 
    private String message; 
    private Child child; 

    @Id 
    @Column(name = "PARENT_ID", unique = true, nullable = false, precision = 22, scale = 0) 
    @SequenceGenerator(name="pk_sequence", sequenceName="SEQ_PK_PARENT") 
    @GeneratedValue(generator="pk_sequence", strategy=GenerationType.SEQUENCE) 
    public Long getParentId() { 
     return this.parentId; 
    } 

    public void setParentId(Long parentId) { 
     this.parentId = parentId; 
    } 

    @Column(name = "MESSAGE", length = 50) 
    public String getMessage() { 
     return this.message; 
    } 

    public void setMessage(String message) { 
     this.message = message; 
    } 

    @OneToOne (cascade = CascadeType.ALL) 
    @PrimaryKeyJoinColumn(name="PARENT_ID", referencedColumnName="CHILD_ID") 
    public Child getTestOneToOneChild() { 
     return this.child; 
    } 

    public void setTestOneToOneChild(Child child) { 
     this.child = child; 
    } 
} 

小孩:

@Entity 
@Table(name = "TEST_ONE_TO_ONE_CHILD", schema = "EXTUSER") 
public class Child implements java.io.Serializable {  
    private static final long serialVersionUID = 1L; 
    private Long childId;  

    private String message; 

    public Child() { 
    } 

    public Child(String message) { 
     this.message = message; 
    } 

    @Id 
    @Column(name = "CHILD_ID")  
    public Long getChildId() { 
     return this.childId; 
    } 

    public void setChildId(Long childId) { 
     this.childId = childId; 
    } 

    @Column(name = "MESSAGE", length = 50) 
    public String getMessage() { 
     return this.message; 
    } 

    public void setMessage(String message) { 
     this.message = message; 
    } 
} 

我完全看到JPA不知道如何爲孩子分配ID的問題。不過我也嘗試過使用Hibernates的「外部」鍵生成器也沒有成功,因爲那個人需要有一個不希望的從父母的父引用。 這個問題對我來說並不罕見,所以我在這裏錯過了什麼?有沒有解決方案?如果純粹的JPA沒有提供解決方案,我也可以使用hibernate擴展。

我對正確行爲的期望是: 如果我試圖堅持附有孩子的父母:從序列

  1. 獲取ID,將其設置在父
  2. 堅持父
  3. 集父母的兒童ID
  4. 堅持孩子

如果我試圖堅持「獨立」孩子(例如entityManager.persist(aChild))我期望一個RuntimeException。

任何幫助,非常感謝!

+0

我還沒有嘗試過,但它可能工作,因爲你把註釋放在getter上,它應該告訴Hibernate用setter填充你的POJO。您可以嘗試在setParentId()和setChild()方法中設置子標識(如果它還沒有)。 – 2011-01-21 08:08:22

+0

不能,這是行不通的:IdentifierGenerationException:這個類的id必須在調用save之前手動賦值save() – Korgen 2011-01-21 08:30:33

+0

您使用的是哪個版本的Hibernate? – axtavt 2011-01-21 13:22:33

回答

3

,因爲人都需要有從孩子一回參考父這是不可取

好吧,如果如果有一個父項的子只能存在,那麼的關係它們之間。你可能不想在OO中表達,但它確實存在於關係模型中。

這就是說,我認爲自然的解決辦法是在孩子中有一個父母。

但是,如果你真的不想這樣做,我會建議看一下將ID映射爲PK類,並使用@EmbeddedId與兩個類共享它們。我很肯定它可以解決你的問題,但有一個例外:

如果我試圖堅持一個「獨立」的孩子(例如entityManager.persist(aChild)),我期望一個RuntimeException。

如果您決定在PK類中使用@EmbeddedId方法,我認爲您需要將上述情況作爲「業務規則」來處理。

4

對於您所描述的DB模式,你可以依賴類使用註解(你的孩子類)來實現映射回父,就像這樣:

@Entity 
class Parent { 
    @Id 
    @Column(name = "parent_id") 
    @GeneratedValue 
    Long parent_id; 
} 

@Entity 
class Child { 
    @Id 
    @Column(name = "child_id") 
    Long child_id; 

    @MapsId 
    @OneToOne 
    @JoinColumn(name = "child_id") 
    Parent parent; 
} 

添加映射從父孩子,你使用@PrimaryKeyJoinColumn註解,你已經上市,使得完整的雙向一到一個映射是這樣的:

@Entity 
class Parent { 
    @Id 
    @Column(name = "parent_id") 
    @GeneratedValue 
    Long parent_id; 

    @OneToOne 
    @PrimaryKeyJoinColumn(name="parent_id", referencedColumnName="child_id") 
    public Child; 
} 

@Entity 
class Child { 
    @Id 
    @Column(name = "child_id") 
    Long child_id; 

    @MapsId 
    @OneToOne 
    @JoinColumn(name = "child_id") 
    Parent parent; 
} 

我用領域,而不是訪問方法(和刪除任何多餘的關係),但是這將是適用於您的獲得者的相同註釋。

對於@MapsId的另一個示例,請參閱2.2.3.1 here的最後一位。

0

此問題的解決方案是在父實體上使用@PostPersist註釋。 您必須在父實體類中創建一個方法並使用@PostPersist對其進行註釋,以便在父實體持久存在後調用此方法,並且在此方法中只需設置子實體類的ID。看下面的例子。

@Entity 
@Table(name = "PARENT") 
public class Parent implements java.io.Serializable {  
    private Long parentId; 
    private String message; 
    private Child child; 

    @Id 
    @Column(name = "PARENT_ID", unique = true, nullable = false, precision = 22, scale = 0) 
    @SequenceGenerator(name="pk_sequence", sequenceName="SEQ_PK_PARENT") 
    @GeneratedValue(generator="pk_sequence", strategy=GenerationType.SEQUENCE) 
    public Long getParentId() { 
     return this.parentId; 
    } 

    public void setParentId(Long parentId) { 
     this.parentId = parentId; 
    } 

    @OneToOne (cascade = CascadeType.ALL) 
    @PrimaryKeyJoinColumn 
    public Child getTestOneToOneChild() { 
     return this.child; 
    } 

    public void setTestOneToOneChild(Child child) { 
     this.child = child; 
    } 


    @PostPersist 
    public void initializeCandidateDetailID() 
    { 
     System.out.println("reached here");// for debugging purpose 
     this.child.setChildId(parentId); // set child id here 
     System.out.println("reached here"+Id); // for debugging purpose 
    } 
}