【问题标题】:Hibernate, single table inheritance and using field from superclass as discriminator columnHibernate,单表继承和使用超类中的字段作为鉴别列
【发布时间】:2011-03-15 18:43:52
【问题描述】:

我有以下几种用于休眠实体层次结构的类。我试图有两个具体的子类Sub1ClassSub2Class。它们由MappedSuperClass 中定义的鉴别器列 (field) 分隔。有一个抽象实体类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 的断言错误:它不喜欢一个类既是实体又是映射的超类。如果我从EntitySuperClass 中删除@Entity,则该类不再是实体并且不能被其他实体引用:

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

}

【问题讨论】:

  • 但是为什么要暴露鉴别器列呢?此列通常是您不想公开的“隐藏”实现细节。
  • 鉴别器列已经暴露在 MappedSuperClass 中,它是外部包的一部分。如果可能的话,我想避免修改的包。

标签: java hibernate single-table-inheritance


【解决方案1】:

在我的项目中是这样完成的:

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

        一个基本原则:您实际上不需要从数据库中检索您的鉴别器列。您应该已经在代码中拥有该信息,您可以在 @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; }
        }
        

        【讨论】:

        • 我认为从注解中调用方法在 Java 中是行不通的。
        猜你喜欢
        • 2015-02-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多