我可以删除Hibernate单表继承中的discriminator列吗?

时间:2012-01-02 15:29:51

标签: java hibernate inheritance single-table-inheritance querydsl

我们在应用程序中为每个表使用单表继承。这允许同一应用程序堆栈的不同实例与相同的DAO一起工作,而它们的实体可能略有不同,可能包含该实例特有的信息。抽象类定义基本表结构,如果该实例需要,扩展定义其他列:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Table(name = "client")
public abstract class Client extends AbstractPersistable<Long> {
    // ...
}

申请A:

@Entity
public class ClientSimple extends Client {
    private String name;
    // getter, setter
}

申请B:

@Entity
public class ClientAdvanced extends Client {
    private String description;
    // getter, setter
}

现在,DAO可以与应用程序A和B的Client对象一起使用,但是应用程序B可以为其客户端对象定义其他信息,这些信息可以由应用程序B独有的管理器方法读取:

申请A:

Client client = new ClientSimple();
clientDao.save(client);

申请B:

Client client = new ClientAdvanced();
clientDao.save(client);

不幸的是,这意味着每个表中都有一个DTYPE列(或者我可能选择的任何其他名称)。有没有办法摆脱这个?我们不需要它,它正在耗尽数据库空间......

谢谢!


修改

重要提示:@MappedSuperclass不起作用。我们使用 QueryDSL 作为我们的HQL抽象层。这需要为类型保存查询自动生成查询类型类。但是,只有在使用@Entity注释抽象类时才会正确生成这些内容。

这是必要的,因为我们想要查询抽象类Client,同时实际查询应用程序A中的ClientSimple和应用程序B中的ClientAdvanced

所以在任何应用程序中都可以使用:

query.where(QClient.client.name.equals("something");

并且在应用程序B中这将起作用:

query.where(QClientSimple.client.description.equals("something else");

EDIT2 - 归结

似乎可以归结为:我可以在部署时配置hibernate,以将一个被编入实体的鉴别器类型设置为固定值。因此,在我的示例中,Client在一个应用程序中始终为ClientSimple,在另一个应用程序中为ClientAdvanced,以便我不必将该信息存储在数据库中?

就像我说的:每个应用程序都将是基本应用程序堆栈的一个实例。每个应用程序可能为其本地数据库定义其他列,但所有对象与该实例的类型相同,因此我们保证鉴别器始终相同,使其在数据库中成为冗余,并且是hibernate配置的用例。

3 个答案:

答案 0 :(得分:7)

我知道,这是一个非常古老的问题,但我最近遇到了这个问题,这可能对某人有用。

这可以使用Hibernate的@DiscriminatorFormula注释来完成。以下描述基于书籍Java Persistence with Hibernate,第5.1.3节;相关部分从第202页最后一段的页面开始。

使用@DiscriminatorFormula,您可以提供SQL语句,该语句在从数据库中提取相关行时确定discriminator的值。在您的情况下,它必须是一个简单的字符串,评估一些任意选择的值。为此,您需要确定将用于Client实体的名称。假设您选择“GenericClient”作为entity的名称。这是@Entity注释中应显示为name属性值的名称。因此,完整的示例,在您的情况下将如下所示。

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Table(name = "client")
@DiscriminatorFormula("'GenericClient'")  // *1*
public abstract class Client extends AbstractPersistable<Long> {
    // ...
}

// Application A
@Entity
@DiscriminatorValue("GenericClient")  // *2*
public class SimpleClient extends Client {
    // ...
}


// Application B
@Entity
@DiscriminatorValue("GenericClient")  // *3*
public class AdvancedClient extends Client {
    // ...
}

由' 1 '表示的行是SQL代码段的一部分,它将始终返回'GenericClient'作为其值。 subclasses的{​​{1}}应始终使用Client进行注释。这意味着当Hibernate从DB中获取行时,要构造的对象的类型将始终是@DiscriminatorValue("GenericClient")的特定子类。

如果Client的子类所在的包位于其中,并且子类的名称是固定的

在这种情况下,不需要子类上的Client,您需要做的就是:

@DiscriminatorValue("GenericClient")

子类不需要任何注释。 @Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @Table(name = "client") @DiscriminatorFormula("'com.example.fixed.path.FixedSubClassName'") public abstract class Client extends AbstractPersistable<Long> { // ... } 默认为discriminator-value,它本身默认为完全限定的类名。

注意: entity-name内的SQL语句可以是目标数据库服务器的任何有效@DiscriminatorFormula()语句。

答案 1 :(得分:1)

如果您永远不需要在同一个应用中同时使用ClientSimpleClientAdvanced,则可以将Client声明为@MappedSuperclass而不是@Entity

答案 2 :(得分:1)

在Hibernate中,每个类的单个表层次总是需要一个鉴别器列来区分实体,因为一个层次结构中的所有类都存储在一个表中。 以下是Hibernate Single Table per Class Hierarchy的示例。

但您可能需要考虑不同的层次结构方案,如下所示:

<强> Hibernate Single Table per Subclass

优点

  • 使用此层次结构,不需要对其进行复杂的更改 修改单个父类时的数据库模式。
  • 效果很好 浅层次。

缺点

  • 随着层次结构的增长,可能会导致性能不佳。
  • 构建子类所需的连接数也会增加。

<强> Hibernate Single Table per Concrete class

优点

  • 这是实现继承映射的最简单方法。

缺点

  • 属于父类的数据分散在许多内容中 子类表,表示具体类。
  • 大多数情况下不推荐使用此层次结构。
  • 对父类的更改会反映到大量表
  • 根据父类进行的查询可能会造成很大的影响 选择操作的数量

我建议你看看每个子类的单表方案。虽然我不确定你的具体要求。但这可能有所帮助。