2010-07-14 9 views
9

Tengo los siguientes tipos de clases para la jerarquía de entidad de hibernación. Estoy tratando de tener dos sub clases concretas Sub1Class y Sub2Class. Están separados por una columna de discriminador (field) que se define en MappedSuperClass. Existe una clase de entidad abstracta EntitySuperClass a la que hacen referencia otras entidades. A las otras entidades no les debería importar si están haciendo referencia a Sub1Class o Sub2Class.Hibernar, herencia de tabla única y usar el campo de la superclase como columna discriminadora

¿Esto realmente posible? Actualmente me sale este error (porque definición de columna se hereda dos veces en Sub1Class y en EntitySuperClass):

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

Si añado @MappedSuperClass a EntitySuperClass, error de aserción después que recibo de hiberante: no le gusta si una clase es a la vez Entidad y una superclase mapeada. Si quito @Entity de EntitySuperClass, la clase hay una entidad más tiempo y no pueden ser referenciadas desde otras entidades:

MappedSuperClass es una parte del paquete externo, por lo que si es posible que no se debe cambiar.

Mis clases:

@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

Pero ¿por qué quiere exponer el discriminador ¿columna? Esta columna suele ser un detalle de implementación "oculta" que no desea exponer. –

+0

La columna del discriminador ya ha sido expuesta en MappedSuperClass, que es una parte del paquete externo. Un paquete que quiero evitar modificar, si es posible. –

Respuesta

14

En mi proyecto se hace de esta manera:

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

Y funciona. Creo que su problema es que define innecesariamente campo discriminador en su definición de superclase. Quítalo y funcionará.

12

Para utilizar una columna de discriminador como una propiedad normal, usted debe hacer que esta propiedad de sólo lectura con insertable = false, updatable = false. Como no se puede cambiar MappedSuperClass, es necesario utilizar @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

puede asignar una columna de base de datos sólo una vez que el campo de lectura-escritura (un campo que tiene insertable=true y/o updatable=true) y cualquier número multiplicado como campo de solo lectura (insertable=falseyupdatable=false). Utilizar una columna como @DiscriminatorColumn cuenta como asignación de lectura-escritura, por lo que no puede tener asignaciones de lectura y escritura adicionales.

Hibernate establecer el valor especificado en @DiscriminatorColumn detrás de las escenas en base al instante clase concreta. Si pudiera cambiar ese campo, permitiría modificar el campo @DiscriminatorColumn para que su subclase y valor en el campo no coincidan.

0

Una cosa fundamental: no debería necesitar recuperar su columna discriminante de DB. Ya debe tener esa información dentro del código, que usa en sus etiquetas @DiscriminatorValue. Si necesita leer eso de DB, reconsidere cuidadosamente la forma en que está asignando discriminadores.

Si lo necesita en el objeto de entidad final, una buena práctica puede ser la implementación de una enumeración de valor discriminador y almacenar devolverlo en 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; } 
} 
Cuestiones relacionadas