5

我们对应用程序中的每个表都使用单表继承。这允许同一应用程序堆栈的不同实例与相同的 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 可以处理Client应用程序 A 和 B 的对象,但应用程序 B 可以为其客户端对象定义附加信息,这些信息可由应用程序 B 独有的管理器方法读取:

应用 A:

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

应用 B:

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

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

谢谢!


编辑

需要注意的重要一点:@MappedSuperclass不起作用。我们使用QueryDSL作为我们的 HQL 抽象层。这需要为类型保存查询自动生成查询类型类。然而,这些只有在抽象类被注释时才能正确生成@Entity

这是必需的,因为我们想查询抽象类Client,而实际上ClientSimple在应用程序 A 和ClientAdvanced应用程序 B 中查询:

因此,在任何应用程序中,这都会起作用:

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

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

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

EDIT2 - 归结为

似乎可以归结为:我可以在部署时配置休眠以将继承实体的鉴别器类型设置为固定值。因此,以我的示例为例,aClient将始终ClientSimple在一个应用程序中,而在另一个应用程序ClientAdvanced中,这样我就不必将该信息存储在数据库中?

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

4

3 回答 3

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”作为其值。的subclassesClient始终用 注释@DiscriminatorValue("GenericClient")。这意味着当 Hibernate 从 DB 中获取行时,要构造的对象的类型将始终是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()可以是SQL目标数据库服务器的任何有效语句。

于 2014-01-24T11:37:10.007 回答
2

如果您从不需要同时使用两者ClientSimple,并且ClientAdvanced在同一个应用程序中,您可以声明Client@MappedSuperclass而不是@Entity.

于 2012-01-02T15:37:20.527 回答
1

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

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

每个子类休眠单个表

好处

  • 使用这种层次结构,在修改单个父类时不需要对数据库模式进行复杂的更改。
  • 它适用于浅层次结构。

缺点

  • 随着层次结构的增长,可能会导致性能下降。
  • 构造子类所需的连接数量也在增加。

每个具体类的休眠单表

好处

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

缺点

  • 属于父类的数据分散在许多代表具体类的子类表中。
  • 大多数情况下不建议使用此层次结构。
  • 对父类的更改会反映到大量表中
  • 以父类为形式的查询很可能会导致大量的选择操作

我建议你看看 Single Table Per Subclass 方案。虽然我不确定你的确切要求。但这可能会有所帮助。

于 2012-01-03T04:26:51.317 回答