我可以删除 Hibernate 单表继承中的鉴别器列吗?

发布于 2024-12-23 16:27:27 字数 1896 浏览 0 评论 0原文

我们对应用程序中的每个表都使用单表继承。这允许同一应用程序堆栈的不同实例使用相同的 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");

编辑2 - 归结

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

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

We use single table inheritance for every table in our application. This allows different instances of the same application stack to work with the same DAOs while their entities might differ slightly potentially containing information unique to that instance. An abstract class defines the basic table structure and an extension defines additional columns, if needed by that instance:

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

application A:

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

application B:

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

Now a DAO can work with Client objects for application A and B but application B can define additional information for its client object that may be read by a manager method unique to application B:

application A:

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

application B:

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

Unfortunately this means there is a DTYPE column in every table (or any other name that I might choose). Is there any way to get rid of this? We don't need it and it's using up DB space...

Thanks!


EDIT

Important to note: @MappedSuperclass won't work. We're using QueryDSL as our HQL abstraction layer. This requires automatically generated Query Type classes for type save querying. These however will only be generated correctly if the abstract class is annotated with @Entity.

This is neccessairy because we want to query against the abstract class Client while in truth querying ClientSimple in application A and ClientAdvanced in application B:

So in any application this will work:

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

and in application B this will work:

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

EDIT2 - boil down

It seems to boil down to this: Can I configure hibernate at deploy time to set the discriminator type for an inhertited entity to a fixed value. So going with my example a Client will always be ClientSimple in one application and ClientAdvanced in the other so that I don't have to store that information in the database?

Like I said: Each application will be an instance of the base application stack. Each application might define additional columns for their local database but ALL objects will be of the same type for that instance so we guarantee that the discriminator is always the same making it redundant in the database and a use case for hibernate configuration.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

念﹏祤嫣 2024-12-30 16:27:27

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

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

@DiscriminatorFormula 您可以提供一个 SQL 语句来确定 discriminator 的值,同时从 数据库。在您的情况下,它必须是一个简单的字符串,其计算结果为某个任意选择的值。为此,您需要决定用于您的 Client 实体的名称。假设您选择“GenericClient”作为实体的名称。这是应出现在 @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”作为其值。 Client子类 应始终使用 @DiscriminatorValue("GenericClient") 进行注释。这意味着当 Hibernate 从数据库获取行时,要构造的对象的类型将始终是 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,它本身默认为完全限定的类名。

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

I know, this is a very old question, but I encountered this problem recently and this might prove useful to someone.

This can be done using Hibernate's @DiscriminatorFormula annotation. The following description is based on the book Java Persistence with Hibernate, section 5.1.3; the relevant part begins at page the last paragraph on page 202.

With @DiscriminatorFormula you can provide an SQL statement that determines the value of the discriminator while fetching the relevant rows from the database. In your case, it would have to be a simple string that evaluates to some arbitrarily selected value. For this to work, you need to decide upon a name that would be used for your Client entity. Suppose that you select 'GenericClient' as the name of the entity. This is the name that should appear within @Entity annotation as the value of the name attribute. So, the complete example, in your case would look like the following.

@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 {
    // ...
}

The line that is denoted by '1' is a part of the SQL snippet that will always return 'GenericClient' as its value. The subclasses of the Client should always be annotated with the @DiscriminatorValue("GenericClient"). What this means is that when Hibernate fetches the rows from the DB, the type of the object to be constructed would always be the specific subclass of Client.

If the package where the subclasses of Client reside, and the name of the subclasses are fixed:

In that case, the @DiscriminatorValue("GenericClient") on the sub-classes wouldn't be required, all you would need to do is:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Table(name = "client")
@DiscriminatorFormula("'com.example.fixed.path.FixedSubClassName'")
public abstract class Client extends AbstractPersistable<Long> {
    // ...
}

The subclasses wouldn't need any annotations. The discriminator-value defaults to the entity-name, which itself defaults to the fully-qualified class-name.

Note: The SQL statement inside @DiscriminatorFormula() can be any valid SQL statement for your targeted DB server.

乖乖兔^ω^ 2024-12-30 16:27:27

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

If you never need to use both ClientSimple and ClientAdvanced in the same application you can declare Client as @MappedSuperclass rather than @Entity.

深爱成瘾 2024-12-30 16:27:27

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

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

Hibernate 每个子类单个表

优点

  • 使用此层次结构,不需要对
    修改单个父类时的数据库架构。
  • 效果很好
    具有浅层次结构。

缺点

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

Hibernate Single每个具体类一个表

优点

  • 这是最容易实现的继承映射方法。

缺点

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

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

In Hibernate, Single Table per Class hierarchy would always need a discriminator column to distinguish between the entities as all classes in one hierarchy are stored in one table.
Here is an example of Hibernate Single Table per Class Hierarchy.

But you may want to consider a different Hierarchy scheme like below:

Hibernate Single Table per Subclass

Advantages

  • Using this hierarchy, does not require complex changes to the
    database schema when a single parent class is modified.
  • It works well
    with shallow hierarchy.

Disadvantages

  • As the hierarchy grows, it may result in poor performance.
  • The number of joins required to construct a subclass also grows.

Hibernate Single Table per Concrete class

Advantages

  • This is the easiest method of Inheritance mapping to implement.

Disadvantages

  • Data thats belongs to a parent class is scattered across a number of
    subclass tables, which represents concrete classes.
  • This hierarchy is not recommended for most cases.
  • Changes to a parent class is reflected to large number of tables
  • A query couched in terms of parent class is likely to cause a large
    number of select operations

I would suggest you to have a look at Single Table Per Subclass scheme. Although I am not sure about your exact requirement. But this may help.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文