Hibernate, single table inheritance and using field from superclass as discriminator column

33,876

Solution 1

In my project it is done this way:

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

And it works. I think your problem is that you needlessly define discriminator field in your superclass definition. Remove it and it will work.

Solution 2

In order to use a discriminator column as a normal property you should make this property read-only with insertable = false, updatable = false. Since you can't change MappedSuperClass, you need to use @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 { 
    ...
}

Solution 3

You can map a database column only once as read-write field (a field that has insertable=true and/or updatable=true) and any number times as read-only field (insertable=false and updatable=false). Using a column as @DiscriminatorColumn counts as read-write mapping, so you can't have additional read-write mappings.

Hibernate will set value specified in @DiscriminatorColumn behind the scenes based on the concrete class instance. If you could change that field, it would allow modifying the @DiscriminatorColumn field so that your subclass and value in the field may not match.

Share:
33,876
Juha Syrjälä
Author by

Juha Syrjälä

GitHublinkedin

Updated on July 17, 2022

Comments

  • Juha Syrjälä
    Juha Syrjälä almost 2 years

    I have following kinds of classes for hibernate entity hierarchy. I am trying to have two concrete sub classes Sub1Class and Sub2Class. They are separated by a discriminator column (field) that is defined in MappedSuperClass. There is a abstract entity class EntitySuperClass which is referenced by other entities. The other entities should not care if they are actually referencing Sub1Class or Sub2Class.

    It this actually possible? Currently I get this error (because column definition is inherited twice in Sub1Class and in EntitySuperClass) :

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

    If I add @MappedSuperClass to EntitySuperClass, then I get assertion error from hiberante: it does not like if a class is both Entity and a mapped super class. If I remove @Entity from EntitySuperClass, the class is no longer entity and can't be referenced from other entities:

    MappedSuperClass is a part of external package, so if possible it should not be changed.

    My classes:

    @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;
        }
    
    }
    
  • Clint Eastwood
    Clint Eastwood about 4 years
    I don't think that calling a method from within an annotation would work in Java.