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

时间:2010-07-14 10:13:18

标签: java hibernate single-table-inheritance

我有以下几种用于hibernate实体层次结构的类。我想要有两个具体的子类Sub1ClassSub2Class。它们由field中定义的鉴别器列(MappedSuperClass)分隔。有一个抽象的实体类EntitySuperClass,由其他实体引用。其他实体不应该关心它们是否实际引用Sub1ClassSub2Class

这实际上可能吗?目前我收到此错误(因为列定义在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中删除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;
    }

}

4 个答案:

答案 0 :(得分:15)

在我的项目中,这样做:

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

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

答案 1 :(得分:13)

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

答案 2 :(得分:2)

您只能将数据库列映射一次作为读写字段(具有insertable=true和/或updatable=true的字段)和任意次数作为只读字段(insertable=false updatable=false)。使用@DiscriminatorColumn列作为读写映射计数,因此您无法进行其他读写映射。

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

答案 3 :(得分:0)

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

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