Спящий режим, наследование одной таблицы и использование поля из суперкласса в качестве столбца дискриминатора

У меня есть следующие виды классов для иерархии сущностей гибернации. Я пытаюсь иметь два конкретных подкласса Sub1Class и Sub2Class. Они разделены столбцом дискриминатора (field), определенным в MappedSuperClass. Существует абстрактный класс сущностей EntitySuperClass, на который ссылаются другие сущности. Другим сущностям все равно, ссылаются ли они на Sub1Class или Sub2Class.

Это действительно возможно? В настоящее время я получаю эту ошибку (поскольку определение столбца наследуется дважды в Sub1Class и в EntitySuperClass):

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

Если я добавлю @MappedSuperClass к EntitySuperClass, то я получаю ошибку утверждения от hiberante: ему не нравится, если класс является одновременно Entity и сопоставленным суперклассом. Если я удалю @Entity из EntitySuperClass, класс перестанет быть сущностью и на него нельзя будет ссылаться из других сущностей:

MappedSuperClass является частью внешнего пакета, поэтому по возможности его не следует менять.

Мои занятия:

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

}

person Juha Syrjälä    schedule 14.07.2010    source источник
comment
Но почему вы хотите выставить столбец дискриминатора? Этот столбец обычно представляет собой скрытую информацию о реализации, которую вы не хотите раскрывать.   -  person Pascal Thivent    schedule 14.07.2010
comment
Столбец дискриминатора уже представлен в MappedSuperClass, который является частью внешнего пакета. Пакет, который я не хочу изменять, если это возможно.   -  person Juha Syrjälä    schedule 15.07.2010


Ответы (4)


В моем проекте это делается так:

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

И это работает. Я думаю, ваша проблема в том, что вы без необходимости определяете поле дискриминатора в своем определении суперкласса. Удалите его, и он будет работать.

person Tadeusz Kopec    schedule 14.07.2010

Чтобы использовать столбец дискриминатора в качестве обычного свойства, вы должны сделать это свойство доступным только для чтения с помощью insertable = false, updatable = false. Поскольку вы не можете изменить MappedSuperClass, вам нужно использовать @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 { 
    ...
}
person axtavt    schedule 14.07.2010

Вы можете отображать столбец базы данных только один раз как поле для чтения и записи (поле, которое имеет insertable=true и/или updatable=true) и любое количество раз как поле только для чтения (insertable=false и updatable=false). Использование столбца как @DiscriminatorColumn считается сопоставлением для чтения и записи, поэтому у вас не может быть дополнительных сопоставлений для чтения и записи.

Hibernate установит значение, указанное в @DiscriminatorColumn за кулисами, на основе конкретного экземпляра класса. Если бы вы могли изменить это поле, это позволило бы изменить поле @DiscriminatorColumn, чтобы ваш подкласс и значение в поле могли не совпадать.

person Juha Syrjälä    schedule 15.07.2010

Один из основных принципов: вам фактически не нужно извлекать столбец дискриминатора из БД. У вас уже должна быть эта информация в коде, которую вы используете в своих тегах @DiscriminatorValue. Если вам нужно прочитать это из БД, внимательно изучите способ назначения дискриминаторов.

Если вам это нужно в конечном объекте объекта, хорошей практикой может быть реализация Enum из значения дискриминатора и возврат, сохранение его в поле @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; }
}
person Siamak    schedule 22.11.2013
comment
Я не думаю, что вызов метода из аннотации будет работать в Java. - person Clint Eastwood; 12.03.2020