我可以删除 Hibernate 单表继承中的鉴别器列吗?
我们对应用程序中的每个表都使用单表继承。这允许同一应用程序堆栈的不同实例使用相同的 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我知道,这是一个非常古老的问题,但我最近遇到了这个问题,这可能对某人有用。
这可以使用 Hibernate 的 @DiscriminatorFormula 注释。以下描述基于Java Persistence with Hibernate一书,第5.1.3节;相关部分从第 202 页的最后一段开始。
@DiscriminatorFormula 您可以提供一个
SQL
语句来确定discriminator
的值,同时从 数据库。在您的情况下,它必须是一个简单的字符串,其计算结果为某个任意选择的值。为此,您需要决定用于您的Client
实体的名称。假设您选择“GenericClient”作为实体
的名称。这是应出现在@Entity
注释作为
name
属性的值。因此,在您的情况下,完整的示例如下所示。由“1”表示的行是 SQL 代码片段的一部分,该代码片段始终返回“GenericClient”作为其值。
Client
的子类
应始终使用@DiscriminatorValue("GenericClient")
进行注释。这意味着当 Hibernate 从数据库获取行时,要构造的对象的类型将始终是Client
的特定子类。如果
Client
的子类所在的包,并且子类的名称是固定的:此时,
@DiscriminatorValue("GenericClient") ,您需要做的就是:
子类不需要任何注释。
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 thediscriminator
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 yourClient
entity. Suppose that you select 'GenericClient' as the name of theentity
. This is the name that should appear within@Entity
annotation as the value of thename
attribute. So, the complete example, in your case would look like the following.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 theClient
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 ofClient
.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:The subclasses wouldn't need any annotations. The
discriminator-value
defaults to theentity-name
, which itself defaults to the fully-qualified class-name.Note: The
SQL
statement inside@DiscriminatorFormula()
can be any validSQL
statement for your targeted DB server.如果您不需要在同一个应用程序中同时使用
ClientSimple
和ClientAdvanced
,您可以将Client
声明为@MappedSuperclass
而不是@Entity
。If you never need to use both
ClientSimple
andClientAdvanced
in the same application you can declareClient
as@MappedSuperclass
rather than@Entity
.在 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
database schema when a single parent class is modified.
with shallow hierarchy.
Disadvantages
Hibernate Single Table per Concrete class
Advantages
Disadvantages
subclass tables, which represents concrete classes.
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.