2015-09-28 8 views
7

Ich habe ein seltsames Problem, wo Hibernate den erwarteten Entitätstyp in einer Beziehung viele zu eins nicht erstellt. Wir haben die folgenden Organisationen, die mit der Unterklasse Hierarchie (vereinfacht):Hibernate erstellt falsche Entity Subtype in Beziehung

@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() { ... } 
} 

In persistence.xml beide Einheiten in der Reihenfolge deklariert werden

A2 
A1 
B2 
B1 

Jetzt habe ich Instanzen von A1 und B1 in der DB zu erstellen:

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

Ich kann sehen, die Instanzen sind in der DB korrekt gespeichert haben jeweils ID 1, DISCRIMINATOR ist auch 1, A_ID in B ist auch 1.

Wenn ich jetzt versuchen, die B zu erhalten (in einer anderen Hibernate Session):

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

bekomme ich die Ausnahme:

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 

Mit Debugging Ich fand heraus, dass Hibernate ist die richtige Einheit zu schaffen, Geben Sie B1 ein, und erstellen Sie eine falsche Entität des Typs A2 für die Beziehung zu A. Der richtige Typ A1 wird erstellt, wenn die Reihenfolge in persistence.xml geändert wird. Es scheint, dass Hibernate die DISCRIMINATOR-Spalte von A-Tabelle in diesem Fall nicht berücksichtigt, sondern immer den ersten in der Konfiguration deklarierten Subtyp erstellt. Wie kann das behoben werden? Stimmt etwas nicht mit den Anmerkungen?

(Ich hatte auch die konkrete Umsetzung der Methode getA() mit seinen Anmerkungen in der Supertyp B auf den ersten, aber dies führt zu ähnlichen Problemen.)

Antwort

3

Mit Hibernate 5.0.2.Final konnte ich Ihr Beispiel arbeiten mit @ManyToOne(..., targetEntity = A.class). Ich ersetzte auch public abstract AClass getA(); durch einen gewöhnlichen Getter.

@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() 
} 

Ich habe nichts über dieses Verhalten in der Dokumentation.So habe ich meine Beobachtungen nur:

  • Ohne targetEntity = A.class Hibernate nicht einmal abfragen, um die DISCRIMINATOR Spalte der Tabelle A wenn eifrig Reihen von A zusammen mit B holen, wie es bereits eine Entscheidung über konkrete Art der A gemacht.
  • Als ich hinzugefügt targetEntity = A.class, A.DISCRIMINATOR erschien in den Abfragen, und Objekte wurden mit den richtigen Unterklassen der Klasse A erstellt.
+0

Danke für Ihre Antwort. Gestern hatte ich die Möglichkeit dies zu testen und es funktioniert im Szenario der Frage :) – Gandalf

3

Sie verwenden die gleichen Spalte verbinden (A_ID) in beiden B1 und B2 Unterklassen.

Verwenden Sie unterschiedliche eine in jeder Unterklasse:

@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() { ... } 
} 

Obwohl es sinnvoll sein, die Spalte wieder zu verwenden (mit unterschiedlichen Spalten wird man sowieso null auf der Unterklasse je für jeden Datensatz sein), so scheint es, dass Hibernate Anwendungen Spaltennamen intern, um einige Mapping-Elemente innerhalb derselben Tabelle eindeutig zu identifizieren. Daher ignoriert es wahrscheinlich die Definition der Viele-zu-Eins-Abbildung in B1 und verwendet auch die aus B2 (weil B2 vor B1 in der persistence.xml definiert ist).

+0

Danke Dragan für Ihre Antwort! :) Ich hatte nicht die Zeit zu testen, ob es funktioniert, weil die andere Antwort funktioniert und einige Vorteile hat: 1. Weniger Code. 2. Weniger DB-Spalten 3. Wir können die Nicht-Null- und Fremdschlüsselbeschränkungen für die DB-Spalte beibehalten. – Gandalf

Verwandte Themen