2010-07-14 6 views
9

Ich habe folgende Arten von Klassen für Hibernate Entity-Hierarchie. Ich versuche, zwei konkrete Unterklassen und Sub2Class zu haben. Sie sind durch eine Diskriminatorsäule (field) getrennt, die in MappedSuperClass definiert ist. Es gibt eine abstrakte Entitätsklasse EntitySuperClass, auf die von anderen Entitäten verwiesen wird. Die anderen Entitäten sollten sich nicht darum kümmern, ob sie tatsächlich auf oder Sub2Class verweisen.Hibernate, Vererbung einzelner Tabellen und Verwendung von Feld aus der Oberklasse als Diskriminatorspalte

Ist das tatsächlich möglich? Derzeit bekomme ich diesen Fehler (weil Spaltendefinition zweimal in Sub1Class vererbt wird und in EntitySuperClass):

Repeated column in mapping for entity: my.package.Sub1Class column: field (should be mapped with insert="false" update="false") 

Wenn ich @MappedSuperClass zu EntitySuperClass hinzufügen, dann bekomme ich Assertionsfehler von hiberante: es nicht mag, wenn eine Klasse beides Entity und eine zugeordnete Superklasse. Wenn ich @Entity von EntitySuperClass zu entfernen, ist die Klasse nicht mehr Einheit und kann nicht von anderen Einrichtungen verwiesen werden:

MappedSuperClass ein Teil der Umverpackung ist, so dass, wenn möglich, sollte nicht verändert werden.

Meine Klassen:

@MappedSuperclass 
public class MappedSuperClass { 
    private static final String ID_SEQ = "dummy_id_seq"; 
    @Id 
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = ID_SEQ) 
    @GenericGenerator(name=ID_SEQ, strategy="sequence") 

    @Column(name = "id", unique = true, nullable = false, insertable = true, updatable = false) 
    private Integer id; 

    @Column(name="field", nullable=false, length=8) 
    private String field; 

    public Integer getId() { 
     return id; 
    } 
    public void setId(Integer id) { 
     this.id = id; 
    } 
    public String getField() { 
     return field; 
    } 
    public void setField(String field) { 
     this.field = field; 
    } 
} 


@Entity 
@Table(name = "ACTOR") 
@Inheritance(strategy=InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name="field", discriminatorType=DiscriminatorType.STRING) 
abstract public class EntitySuperClass extends MappedSuperClass { 


    @Column(name="description", nullable=false, length=8) 
    private String description; 

    public String getDescription() { 
     return description; 
    } 

    public void setDescription(String description) { 
     this.description = description; 
    } 
} 

@Entity 
@DiscriminatorValue("sub1") 
public class Sub1Class extends EntitySuperClass { 

} 


@Entity 
@DiscriminatorValue("sub2") 
public class Sub2Class extends EntitySuperClass { 

} 


@Entity 
public class ReferencingEntity { 
    @Id 
    @GeneratedValue(strategy = GenerationType.SEQUENCE) 
    private Integer id; 

    @Column 
    private Integer value; 

    @ManyToOne 
    private EntitySuperClass entitySuperClass; 


    public Integer getId() { 
     return id; 
    } 

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

    public Integer getValue() { 
     return value; 
    } 

    public void setValue(Integer value) { 
     this.value = value; 
    } 

    public EntitySuperClass getEntitySuperClass() { 
     return entitySuperClass; 
    } 

    public void setEntitySuperClass(EntitySuperClass entitySuperClass) { 
     this.entitySuperClass = entitySuperClass; 
    } 

} 
+0

Aber warum Sie den Diskriminator machen möchten Säule? Diese Spalte ist normalerweise ein "ausgeblendetes" Implementierungsdetail, das Sie nicht anzeigen möchten. –

+0

Die Diskriminatorspalte wurde bereits in MappedSuperClass exponiert, die ein Teil des externen Pakets ist. Ein Paket, das ich möglichst nicht modifizieren möchte. –

Antwort

14

In meinem Projekt es auf diese Weise getan wird:

@Entity 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name = "field", discriminatorType = DiscriminatorType.STRING) 
@DiscriminatorValue("dummy") 
public class EntitySuperClass { 
    // here definitions go 
    // but don't define discriminator column here 
} 

@Entity 
@DiscriminatorValue(value="sub1") 
public class Sub1Class extends EntitySuperClass { 
    // here definitions go 
} 

Und es funktioniert. Ich denke, Ihr Problem besteht darin, dass Sie das Diskriminatorfeld in Ihrer Superklassen-Definition unnötig definieren. Entfernen Sie es und es wird funktionieren.

12

Um eine Diskriminatorspalte als normale Eigenschaft zu verwenden, sollten Sie diese Eigenschaft mit insertable = false, updatable = false schreibgeschützt machen. Da Sie nicht MappedSuperClass ändern können, müssen Sie @AttributeOverride verwenden:

@Entity 
@Table(name = "ACTOR") 
@Inheritance(strategy=InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name="field", discriminatorType=DiscriminatorType.STRING) 

@AttributeOverride(name = "field", 
    column = @Column(name="field", nullable=false, length=8, 
     insertable = false, updatable = false)) 

abstract public class EntitySuperClass extends MappedSuperClass { 
    ... 
} 
2

Sie ordnen Spalte eine Datenbank nur einmal als Schreib-Lese-Feld (ein Feld, das insertable=true und/oder updatable=true hat) und eine beliebige Anzahl mal so schreibgeschütztes Feld (insertable=falseundupdatable=false). Die Verwendung einer Spalte wie @DiscriminatorColumn gilt als Lese-Schreib-Zuordnung, sodass Sie keine zusätzlichen Lese-/Schreibzuordnungen haben können.

Hibernate stellt den Wert @DiscriminatorColumn hinter den Kulissen basierend auf der konkreten Klasseninstanz ein. Wenn Sie dieses Feld ändern könnten, würde dies das Ändern des Feldes @DiscriminatorColumn ermöglichen, sodass Ihre Unterklasse und der Wert im Feld möglicherweise nicht übereinstimmen.

0

Eine grundlegende: Sie sollten effektiv nicht Ihre Diskriminator-Spalte von DB abrufen müssen. Sie sollten diese Informationen bereits innerhalb des Codes haben, den Sie in Ihren @DiscriminatorValue-Tags verwenden. Wenn Sie das von DB lesen müssen, überdenken Sie sorgfältig die Art, wie Sie Diskriminatoren zuweisen.

Wenn Sie es in final Entitätsobjekt benötigen, kann man gute Praxis sein, eine Enum von Diskriminatorwert zu implementieren und das Rück speichern es in einem @Transient Feld:

@Entity 
@Table(name="tablename") 
@DiscriminatorValue(Discriminators.SubOne.getDisc()) 
public class SubClassOneEntity extends SuperClassEntity { 

    ... 

    @Transient 
    private Discriminators discriminator; 

    // Setter and Getter 
    ... 
} 

public enum Discriminators { 
    SubOne ("Sub1"), 
    SubOne ("Sub2"); 

    private String disc; 
    private Discriminators(String disc) { this.disc = disc; } 
    public String getDisc() { return this.disc; } 
} 
Verwandte Themen