2015-09-28 13 views
7

hibernateが多対1リレーショナルシップで期待されるエンティティタイプを作成しないという奇妙な問題があります。私たちは、サブクラス階層(簡体字)で以下のエンティティを持っている:Hibernateが関係で間違ったエンティティサブタイプを作成しています

@Entity 
@Table(name = "A") 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING, length = 1) 
public abstract class A { 

    @Id 
    ... 
    public Long getId() { ... } 
    ... 
} 

@Entity 
@DiscriminatorValue("1") 
public class A1 extends A { 
    ... 
} 

@Entity 
@DiscriminatorValue("2") 
public class A2 extends A { 
    ... 
} 


@Entity 
@Table(name = "B") 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING, length = 1) 
public abstract class B<AClass extends A> { 

    protected AClass a; 

    @Id 
    ... 
    public Long getId() { ... } 
    ... 

    public abstract AClass getA(); 
    public void setA(AClass a) { ... } 
} 

@Entity 
@DiscriminatorValue("1") 
public class B1 extends B<A1> { 
    ... 

    @Override 
    @ManyToOne(fetch = EAGER) 
    @JoinColumn(name = "A_ID") 
    public A1 getA() { ... } 
} 

@Entity 
@DiscriminatorValue("2") 
public class B2 extends B<A2> { 
    ... 

    @Override 
    @ManyToOne(fetch = EAGER) 
    @JoinColumn(name = "A_ID") 
    public A2 getA() { ... } 
} 

persistence.xmlで両方のエンティティが順番

A2 
A1 
B2 
B1 

で宣言されている今、私はA1とDBにおけるB1のインスタンスを作成します。

A1 a1 = new A1(); 
entityManager.persist(a1); 
B1 b1 = new B1(); 
b1.setA(a1); 
entityManager.persist(b1); 

インスタンスがそれぞれID 1、DISCRIMINATORも1、BのA_IDも正しく1に格納されていることがわかります。

私は今、(他のHibernateセッションで)Bを取得しようとすると:

B b = entityManager.find(B.class, 1L); 

私は例外を取得:デバッグに

org.hibernate.PropertyAccessException: Exception occurred inside getter of B 
Caused by: java.lang.ClassCastException: A2 cannot be cast to A1 
at B1.getA(B1.java:61) 
... 108 more 

私はHibernateが正しいエンティティを作成していることが判明B1と入力し、Aとの関係にタイプA2の不正なエンティティを作成します。persistence.xmlの注文が変更された場合、正しいタイプA1が作成されます。この場合、hibernateはAテーブルのDISCRIMINATORカラムを考慮しないように思われますが、常に設定で宣言された最初のサブタイプが作成されます。どのようにこれを修正することができますか?注釈に何か問題がありますか?

(私も最初はスーパータイプBでその注釈とメソッドgetA()の具体的な実装を持っていたが、これは同様の問題につながる。)

答えて

3

Hibernate 5.0.2.Finalを使用して、@ManyToOne(..., targetEntity = A.class)を使用してサンプルを動作させることができました。私もpublic abstract AClass getA();を普通のゲッターに置き換えました。

@Entity 
@Table(name = "B") 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING, length = 1) 
public abstract class B<AClass extends A> { 
    private Long id; 
    private AClass a; 

    @Id 
    @GeneratedValue 
    public Long getId() { 
     return id; 
    } 

    public void setId(Long id) { 
     this.id = id; 
    } 

    @ManyToOne(fetch = FetchType.EAGER, targetEntity = A.class) 
    @JoinColumn(name = "A_ID") 
    public AClass getA() { 
     return a; 
    } 

    public void setA(AClass a) { 
     this.a = a; 
    } 
} 
@Entity 
@DiscriminatorValue("1") 
public class B1 extends B<A1> { 
    // no need to override getA() 
} 
@Entity 
@DiscriminatorValue("2") 
public class B2 extends B<A2> { 
    // no need to override getA() 
} 

私は、ドキュメントでこの動作については何も見つかりませんでした。だから私は私の見解を持っている:それはすでにAの実際の型についての決定をしたように熱心に、BとともにAから行をフェッチするとき

  • targetEntity = A.classなければHibernateはしても、テーブルADISCRIMINATOR列を照会しませんでした。
  • targetEntity = A.classを追加すると、クエリにA.DISCRIMINATORが表示され、オブジェクトはクラスAの右側のサブクラスで作成されました。
+0

あなたの答えをありがとう。昨日私はこれをテストする機会を得て、それは質問のシナリオで動作します:) – Gandalf

3

あなたは、両方のB1に同じ結合列(A_ID)を使用していて、 B2サブクラス。

各サブクラスで

使用異なる1:それは(異なる列に1がとにかくサブクラスに応じて、各レコードのnullになります)、列を再利用しても意味がありますが

@Entity 
@DiscriminatorValue("1") 
public class B1 extends B<A1> { 
    @Override 
    @ManyToOne(fetch = EAGER) 
    @JoinColumn(name = "A1_ID") 
    public A1 getA() { ... } 
} 

@Entity 
@DiscriminatorValue("2") 
public class B2 extends B<A2> { 
    @Override 
    @ManyToOne(fetch = EAGER) 
    @JoinColumn(name = "A2_ID") 
    public A2 getA() { ... } 
} 

、それは、Hibernateが使用するようです列名は内部的に同じテーブル内のいくつかのマッピング要素を一意に識別するために使用されます。そのため、B1でmany-to-oneマッピングの定義を無視し、B2のものを使用します(B2B1の前にpersistence.xmlに定義されているため)。

+0

お返事ありがとうございます! :)他の答えが働いていくつかの利点があるので、私はそれが動作するかどうかテストする時間がなかった:1.少ないコード。 2. DB列の削減3.非NULLキーと外部キー制約をDB列に保持できます。 – Gandalf

関連する問題