Java Hibernate,单表继承并使用来自超类的字段作为鉴别器列

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3245142/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 17:44:13  来源:igfitidea点击:

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

javahibernatesingle-table-inheritance

提问by Juha Syrj?l?

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

我有以下类型的休眠实体层次结构类。我试图有两个具体的子类Sub1ClassSub2Class. 它们由field中定义的鉴别器列 ( )分隔MappedSuperClass。有一个EntitySuperClass被其他实体引用的抽象实体类。其他实体不应该关心它们是否实际引用Sub1ClassSub2Class

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

这实际上可能吗?目前我收到此错误(因为在 Sub1Class 和 EntitySuperClass 中两次继承了列定义):

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

If I add @MappedSuperClassto 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 @Entityfrom EntitySuperClass, the class is no longer entity and can't be referenced from other entities:

如果我添加@MappedSuperClassEntitySuperClass,那么我会收到来自 hiberante 的断言错误:它不喜欢一个类既是实体又是映射的超类。如果我@Entity从 中删除EntitySuperClass,该类不再是实体并且不能被其他实体引用:

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

MappedSuperClass是外部包的一部分,因此如果可能,不应更改它。

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

}

采纳答案by Tadeusz Kopec

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.

它有效。我认为您的问题是您在超类定义中不必要地定义了鉴别器字段。删除它,它将起作用。

回答by axtavt

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:

为了将鉴别器列用作普通属性,您应该使用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 { 
    ...
}

回答by Juha Syrj?l?

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

您只能将数据库列映射一次作为读写字段(具有insertable=true和/或的字段updatable=true)和任意次数作为只读字段(insertable=falseupdatable=false)。使用列作为@DiscriminatorColumn读写映射,因此您不能有额外的读写映射。

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

Hibernate 将@DiscriminatorColumn根据具体的类实例设置在幕后指定的值。如果您可以更改该字段,它将允许修改该@DiscriminatorColumn字段,以便您的子类和该字段中的值可能不匹配。

回答by Siamak

One fundamental: You effectively should not need to retrieve your discriminator column from DB. You should already have that information within the code, of which you use in your @DiscriminatorValue tags. If you need read that from DB, reconsider carefully the way you are assigning discriminators.

一个基本原则:您实际上不应该需要从数据库中检索您的鉴别器列。您应该已经在代码中包含了该信息,您在 @DiscriminatorValue 标签中使用了这些信息。如果您需要从 DB 中读取该信息,请仔细重新考虑分配鉴别器的方式。

If you need it in final entity object, one good practice can be to implement an Enum from discriminator value and return store it in a @Transient field:

如果您需要在最终实体对象中使用它,一个好的做法是从鉴别器值实现 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; }
}