2012-06-01 16 views
20

複数の列を介して結合したい2つのエンティティがあります。これらの列は、両方のエンティティによって共有される@Embeddableオブジェクトによって共有されます。以下の例では、Fooは1つだけBarを持つことができますが、Barは複数のFooAnEmbeddableObjectBarのユニークなキーです)を持つことができます。次に例を示します。Hibernate/JPAアノテーションでの複数列結合

@Entity 
@Table(name = "foo") 
public class Foo { 
    @Id 
    @Column(name = "id") 
    @GeneratedValue(generator = "seqGen") 
    @SequenceGenerator(name = "seqGen", sequenceName = "FOO_ID_SEQ", allocationSize = 1) 
    private Long id; 
    @Embedded 
    private AnEmbeddableObject anEmbeddableObject; 
    @ManyToOne(targetEntity = Bar.class, fetch = FetchType.LAZY) 
    @JoinColumns({ 
     @JoinColumn(name = "column_1", referencedColumnName = "column_1"), 
     @JoinColumn(name = "column_2", referencedColumnName = "column_2"), 
     @JoinColumn(name = "column_3", referencedColumnName = "column_3"), 
     @JoinColumn(name = "column_4", referencedColumnName = "column_4") 
    }) 
    private Bar bar; 

    // ... rest of class 
} 

、バークラス:

@Entity 
@Table(name = "bar") 
public class Bar { 
    @Id 
    @Column(name = "id") 
    @GeneratedValue(generator = "seqGen") 
    @SequenceGenerator(name = "seqGen", sequenceName = "BAR_ID_SEQ", allocationSize = 1) 
    private Long id; 
    @Embedded 
    private AnEmbeddableObject anEmbeddableObject; 

    // ... rest of class 
} 

最後にAnEmbeddedObjectクラス:

@Embeddable 
public class AnEmbeddedObject { 
    @Column(name = "column_1") 
    private Long column1; 
    @Column(name = "column_2") 
    private Long column2; 
    @Column(name = "column_3") 
    private Long column3; 
    @Column(name = "column_4") 
    private Long column4; 

    // ... rest of class 
} 

明らかスキーマはそれはAnEmbeddedObject制限され、不十分正規化されます各フィールドでフィールドが繰り返されます。私はJoinColumnsをマーキングしようとしている

org.hibernate.AnnotationException: referencedColumnNames(column_1, column_2, column_3, column_4) of Foo.bar referencing Bar not mapped to a single property 

に挿入し、更新可能ではありませんが、運と:

私が持っている問題は、私は私が休止状態を起動しようとすると、このエラーが表示されるということです。これをHibernate/JPAアノテーションで表現する方法はありますか?

+0

「Foo」から埋め込みファイルを削除するとどうなりますか? – siebz0r

答えて

9

これが機能しない場合、私はアイデアがありません。このようにして、両方のテーブルの4つのカラムを取得します(Barはそれらを所有し、Fooはそれらを参照のために使用します)Barと両方のエンティティで生成されたID。多列関係は多対多にならないように、4列の集合はBarで一意でなければなりません。

@Embeddable 
public class AnEmbeddedObject 
{ 
    @Column(name = "column_1") 
    private Long column1; 
    @Column(name = "column_2") 
    private Long column2; 
    @Column(name = "column_3") 
    private Long column3; 
    @Column(name = "column_4") 
    private Long column4; 
} 

@Entity 
public class Foo 
{ 
    @Id 
    @Column(name = "id") 
    @GeneratedValue(generator = "seqGen") 
    @SequenceGenerator(name = "seqGen", sequenceName = "FOO_ID_SEQ", allocationSize = 1) 
    private Long id; 
    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumns({ 
     @JoinColumn(name = "column_1", referencedColumnName = "column_1"), 
     @JoinColumn(name = "column_2", referencedColumnName = "column_2"), 
     @JoinColumn(name = "column_3", referencedColumnName = "column_3"), 
     @JoinColumn(name = "column_4", referencedColumnName = "column_4") 
    }) 
    private Bar bar; 
} 

@Entity 
@Table(uniqueConstraints = @UniqueConstraint(columnNames = { 
    "column_1", 
    "column_2", 
    "column_3", 
    "column_4" 
})) 
public class Bar 
{ 
    @Id 
    @Column(name = "id") 
    @GeneratedValue(generator = "seqGen") 
    @SequenceGenerator(name = "seqGen", sequenceName = "BAR_ID_SEQ", allocationSize = 1) 
    private Long id; 
    @Embedded 
    private AnEmbeddedObject anEmbeddedObject; 
} 
+0

それは擦れて、 "AnEmbeddedObject"は両方のオブジェクトに存在しなければなりません。生成されたIDは、パフォーマンス上の理由から好ましいものです。 – bowsie

+0

4列に加えて生成されたキーマップはバーになりますか?それは非常に非常に悪いです。あなたの解決策が 'Bar'の代理キーを生成し、' Foo'の外部キーに列を作成しない場合は、 'Foo'の列を自分で設定する必要があります。私が正しいなら私は答えを調整することができます。 ;-) – siebz0r

+0

@bowsie「Foo」と「Bar」の両方が4つの列を持ち、「Bar」がそのIDで 'Foo'で参照されるように、私は自分の答えを修正しました。 – siebz0r

4

ハイバーネイトは、あなたがしようとしていることをあなたが容易にするつもりはありません。 Hibernate documentationから:

非公開キー列にreferencedColumnNameを使用する場合、関連付けられたクラスはシリアライズ可能でなければならないことに注意してください。 また、非プライマリキー列のreferencedColumnNameは、単一の列を持つプロパティにマップする必要があります(他の場合は機能しない場合があります)。(強調追加)

あなたがバーのためAnEmbeddableObject識別子を作るために不本意であればそれではHibernateは遅延し、自動的にあなたのためのバーを取得するつもりはありません。もちろん、HQLを使用してAnEmbeddableObjectに参加するクエリを書くことはできますが、Barに複数の列の非主キーを使用することを主張する場合、自動フェッチとライフサイクルのメンテナンスは失われます。

+0

私は理論的には、1つの固有の制約を参照する複数の外部キーを使用することは可能であるはずです。 OPの運が悪いのでなければ。 +1これはおそらく答えです。 – siebz0r

+0

@ siebz0r、OPはショットを撮って、私がこのエラー状態のために期待するエラーメッセージ、「referencedColumnNames ... ***は単一のプロパティ***にマップされていません。それは "単一の列を持つプロパティにマップされていない"と言うべきですが、それはほぼ同じことです。 –

+0

私はそれ以上のゲームだと思う;-) – siebz0r

10

これは私のために働いた。私の場合、2つのテーブルfooとbooは3つの異なるカラムに基づいて結合されなければなりません。私のケースでは、3つの共通カラムは主キーではありません。

つまり、3つの異なるカラムに基づいて1対1のマッピング

@Entity 
@Table(name = "foo") 
public class foo implements Serializable 
{ 
    @Column(name="foocol1") 
    private String foocol1; 
    //add getter setter 
    @Column(name="foocol2") 
    private String foocol2; 
    //add getter setter 
    @Column(name="foocol3") 
    private String foocol3; 
    //add getter setter 
    private Boo boo; 
    private int id; 
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(name = "brsitem_id", updatable = false) 
    public int getId() 
    { 
     return this.id; 
    } 
    public void setId(int id) 
    { 
     this.id = id; 
    } 
    @OneToOne 
    @JoinColumns(
    { 
     @JoinColumn(updatable=false,insertable=false, name="foocol1", referencedColumnName="boocol1"), 
     @JoinColumn(updatable=false,insertable=false, name="foocol2", referencedColumnName="boocol2"), 
     @JoinColumn(updatable=false,insertable=false, name="foocol3", referencedColumnName="boocol3") 
    } 
    ) 
    public Boo getBoo() 
    { 
     return boo; 
    } 
    public void setBoo(Boo boo) 
    { 
     this.boo = boo; 
    } 
} 





@Entity 
@Table(name = "boo") 
public class Boo implements Serializable 
{ 
    private int id; 
    @Column(name="boocol1") 
    private String boocol1; 
    //add getter setter 
    @Column(name="boocol2") 
    private String boocol2; 
    //add getter setter 
    @Column(name="boocol3") 
    private String boocol3; 
    //add getter setter 
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(name = "item_id", updatable = false) 
    public int getId() 
    { 
     return id; 
    } 
    public void setId(int id) 
    { 
     this.id = id; 
    } 
}