2011-01-21 21 views
10

JPA(Provider:Hibernate)で動作するために単方向の一対一の関係を取得しようとするのは非常に困難です。私の意見では、あまりにも面倒ではないはずですが、明らかにJPA/Hibernateはそれに同意しません;-)共有プライマリキーを使用したJPA/Hibernate単方向1対1マッピング

問題は、私が変更できないレガシースキーマと、このスキーマが共有プライマリ同時に2つのエンティティ間のキーであり、同時に1つのエンティティに対する外部キーである。

私は簡単なテストケースを作成し、次のよう

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; 

次のように親(1対1の=所有している側)になります。

@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の "foreign"キージェネレータを使用してみました。これは成功しませんでした。なぜなら、それは望ましくない子から親への逆参照を必要とするからです。 この問題はあまりにも珍しいようではないので、私はここで何が欠けていますか?解決策はありますか?純粋なJPAが解決策を提供しない場合、私はまた、休止状態の拡張を使用することもできます。正しい動作のための

私の期待は次のようになります。 私は付属の子どもと親を持続しようとした場合:

  1. は親
  2. を持続し、親にそれを設定し、シーケンスからIDを取得します私は、「スタンドアロン」を持続しようとした場合、子
  3. のセット親のIDは子

を持続します子供entityManager.persist(aChild))私はRuntimeExceptionを期待します。

ご協力いただきありがとうございます。

+0

私はそれを試したことはありませんが、getterに注釈を付けると動作するかもしれません。これは、Hibernateにsettersを使ってPOJOを設定するよう指示します。 setParentId()メソッドとsetChild()メソッドで子IDを設定しようとする可能性があります(まだない場合)。 –

+0

いいえ、動作しません:IdentifierGenerationException:save()を呼び出す前にこのクラスのIDを手動で割り当てる必要があります – Korgen

+0

どのバージョンのHibernateを使用しますか? – axtavt

答えて

3

1つは、親があれば子供にのみ存在することができれば、その後、関係がある、まあ

望ましいことではない、子から親への後方参照を持っている必要があるため、それらの間の。 OOで表現したくないかもしれませんが、それはリレーショナル・モデルに存在します。

これは自然な解決策は、子どもに親を持つことだと言います。

本当にそれをしたくないのであれば、IDをPKクラスとしてマッピングし、@EmbeddedIdを使って両方のクラスと共有することをお勧めします。 1つの例外を除いて、あなたの問題を解決することができます:

「スタンドアロン」の子(たとえば、entityManager.persist(aChild))を永続化しようとすると、私はRuntimeExceptionを予想します。

PKクラスで@EmbeddedIdアプローチを使用することに決めた場合、上記のケースを「ビジネスルール」として扱う必要があると思います。

あなたがそうのように、戻って親へのマッピングを達成するために依存クラス(あなたの子クラス)にアノテーションを使用することができますが、説明したDBスキーマのために
+1

Partenon、 ありがとうあなたの答え。あなたは、DBシーケンスから親のIDを受け取った後で、子のEmbeddedIdが設定されることを確かめていますか? – Korgen

4

、:

@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; 
} 

は親からのマッピングを追加しますあなたがリストされていたとして、あなたは完全な双方向の1対1のマッピングは次のようになりながら、@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; 
} 

は、私は、フィールドではなく、メソッドへのアクセスを(使用しての関係に余分なものを取り除きます)、しかしあなたのgetterに適用されるのと同じアノテーションになります。

@MapsIdの別の例については、セクション2.2.3.1 hereの最後のビットも参照してください。

+0

OPは子供の親の参照を望まない。彼は私が手動で扱わなければならないと考える単方向の1対1ソリューションを望んでいました。ここの私の考えhttp://stackoverflow.com/questions/7892571/hibernate-code-exception-in-onetoone-mapping/12318513#12318513 –

+0

ああ、こんにちは。単方向であることを望んでいるというビットは、何らかの理由で登録に失敗しました。 –

+1

この例は、親のPrimaryKeyJoinColumnアノテーションを示していますが、Web上の他のどこでも、これは子でのみ使用されています。まだそれについて困惑している。 –

0

この問題を解決するには、親エンティティで@PostPersist注釈を使用しています。 親エンティティクラスにメソッドを作成し、@PostPersistでアノテーションを付けなければならないため、このメソッドは親エンティティが永続化された後に呼び出され、このメソッドでは子エンティティクラスのIDを設定するだけです。以下の例を参照してください。

関連する問題