2010-07-14 14 views
9

Ho i seguenti tipi di classi per la gerarchia di entità in ibernazione. Sto cercando di avere due sottoclassi concrete Sub1Class e Sub2Class. Sono separati da una colonna discriminatore (field) definita in MappedSuperClass. Esiste una classe di entità astratta EntitySuperClass a cui fanno riferimento altre entità. Le altre entità non dovrebbero preoccuparsi se in realtà fanno riferimento a Sub1Class o Sub2Class.Hibernate, ereditarietà di una singola tabella e utilizzo del campo dalla superclasse come colonna discriminatore

E 'possibile? Attualmente ottengo questo errore (perché la definizione di colonna è ereditato due volte in Sub1Class e in EntitySuperClass):

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

Se aggiungo @MappedSuperClass a EntitySuperClass, allora ottengo errore di asserzione da hiberante: non gli piace se una classe è sia Entità e una super classe mappata. Se rimuovo @Entity dal EntitySuperClass, la classe non è un'entità più e non è possibile fare riferimento da altre entità:

MappedSuperClass è una parte del pacchetto esterno, in modo, se possibile, non dovrebbe essere modificato.

Le mie lezioni:

@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

Ma perché si vuole esporre il discriminatore colonna? Questa colonna è in genere un dettaglio di implementazione "nascosto" che non si desidera esporre. –

+0

La colonna discriminator è già stata esposta in MappedSuperClass, che è una parte del pacchetto esterno. Un pacchetto che voglio evitare di modificare, se possibile. –

risposta

14

Nel mio progetto è fatto in questo modo:

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

E funziona. Penso che il tuo problema sia che devi definire inutilmente il campo discriminatore nella definizione della tua superclasse. Rimuovilo e funzionerà.

12

Per utilizzare una colonna discriminatore come proprietà normale, è necessario rendere questa proprietà di sola lettura con insertable = false, updatable = false. Dal momento che non è possibile modificare MappedSuperClass, è necessario utilizzare @AttributeOverride:

@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

è possibile associare una colonna di database solo una volta come campo di lettura-scrittura (un campo che ha insertable=true e/o updatable=true) e qualsiasi numero di volte come campo di sola lettura (insertable=falseeupdatable=false). L'utilizzo di una colonna come @DiscriminatorColumn conta come un mapping di lettura-scrittura, quindi non è possibile avere ulteriori mapping di lettura-scrittura.

Hibernate imposterà valore specificato nel @DiscriminatorColumn dietro le quinte in base alla istanza di classe concreta. Se è possibile modificare tale campo, consentirebbe di modificare il campo @DiscriminatorColumn in modo che la sottoclasse e il valore nel campo potrebbero non corrispondere.

0

Un fondamentale: è efficace, non dovrebbe essere necessario per recuperare la colonna discriminatore da DB. Dovresti già avere queste informazioni all'interno del codice, di cui usi i tag @DiscriminatorValue. Se hai bisogno di leggerlo dal DB, riconsiderare attentamente il modo in cui assegni i discriminatori.

Se avete bisogno in oggetto entità finale, una buona pratica può essere quello di implementare un Enum dal valore di discriminatore e tornare conservarla in un campo @Transient:

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