我可以删除 Hibernate 单表继承中的鉴别器列吗?
Posted
技术标签:
【中文标题】我可以删除 Hibernate 单表继承中的鉴别器列吗?【英文标题】:Can I remove the discriminator column in a Hibernate single table inheritance? 【发布时间】:2012-01-02 15:29:51 【问题描述】:我们对应用程序中的每个表都使用单表继承。这允许同一应用程序堆栈的不同实例与相同的 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 - 归结为
似乎可以归结为:我可以在部署时配置休眠以将继承实体的鉴别器类型设置为固定值。因此,以我的示例为例,Client
在一个应用程序中将始终为ClientSimple
,而在另一个应用程序中将始终为ClientAdvanced
,这样我就不必将该信息存储在数据库中?
就像我说的:每个应用程序都是基础应用程序堆栈的一个实例。每个应用程序可能会为其本地数据库定义额外的列,但该实例的所有对象都将属于同一类型,因此我们保证鉴别器始终相同,使其在数据库中成为冗余,并且是休眠配置的用例。
【问题讨论】:
您使用的是哪个 Querydsl 版本? 我们使用的是 QueryDSL 2.2.3,但如果新版本支持映射超类作为查询目标,我们可能会更新:像这样:@MappedSuperclass @Table public abstract class Client ...
+ @Entity public class ClientSimple extends Client
==> 生成查询类型... == > 查询:QClient.client.name
不直接支持,但可以在 GitHub 上为其添加票证。它很容易实现。
好的,做到了。希望尽快实现它;)我们还有一些时间才能开始使用它,但如果我们可以使用 @MappedSuperclass
或使用某种 Hibernate 设置或类覆盖,我们仍然会很高兴..
我知道这不是问的正确地方,但是如果我继承了属于框架的实体,因此我无法更改超类怎么办? Hibernate 正在吐出这个不存在的 DTYPE 列。我该如何避免呢?
【参考方案1】:
我知道,这是一个非常古老的问题,但我最近遇到了这个问题,这可能对某人有用。
这可以使用 Hibernate 的 @DiscriminatorFormula 注释来完成。以下描述基于书Java Persistence with Hibernate,第5.1.3节;相关部分从第 202 页的最后一段开始。
使用@DiscriminatorFormula,您可以提供一个SQL
语句来确定discriminator
的值,同时从数据库中获取相关行。在您的情况下,它必须是一个简单的字符串,可以评估为一些任意选择的值。为此,您需要决定将用于您的Client
实体的名称。假设您选择“GenericClient”作为entity
的名称。这是应该作为name
属性值出现在@Entity
注释中的名称。因此,在您的情况下,完整的示例如下所示。
@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 sn-p 的一部分,它总是返回 'GenericClient' 作为其值。 Client
的 subclasses
应始终使用 @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
,它本身默认为完全限定的类名。
注意:@DiscriminatorFormula()
中的SQL
语句可以是目标数据库服务器的任何有效SQL
语句。
【讨论】:
【参考方案2】:如果您永远不需要在同一个应用程序中同时使用ClientSimple
和ClientAdvanced
,您可以将Client
声明为@MappedSuperclass
而不是@Entity
。
【讨论】:
不幸的是,这是不可能的。我们使用查询 DSL 作为我们的 HQL 抽象层。这需要对抽象类进行注释 @Entity 以便可以针对抽象类运行查询,同时实际上查询扩展。我将编辑我的帖子以反映此限制。【参考方案3】:在 Hibernate 中,每个类层次结构的单个表总是需要一个鉴别器列来区分实体,因为一个层次结构中的所有类都存储在一个表中。 这是Hibernate Single Table per Class Hierarchy 的示例。
但您可能需要考虑不同的层次结构方案,如下所示:
Hibernate Single Table per Subclass
优势
使用此层次结构,不需要对 修改单个父类时的数据库模式。 效果很好 层次结构浅。缺点
随着层次结构的增长,可能会导致性能下降。 构造子类所需的连接数量也在增加。Hibernate Single Table per Concrete class
优势
这是最容易实现的继承映射方法。缺点
属于父类的数据分散在多个 子类表,代表具体类。 不建议在大多数情况下使用此层次结构。 对父类的更改会反映到大量表中 根据父类表达的查询可能会导致大 选择操作数我建议你看一下每个子类的单表方案。虽然我不确定你的确切要求。但这可能会有所帮助。
【讨论】:
你确定没有办法编写一些自定义反序列化器吗?我不是 hibernate 专家,但我敢打赌,如果我们查看 hibernate 将 ResultSet 转换为对象的方式,就会有一种方法可以按照 Pete 想要的方式自定义它。 感谢链接viralpatel,读得很好,但不幸的是它并没有真正帮助。也许这不能只用注释来完成,比如家伙 mograbi 提示......?! 嗨,伙计,可以肯定的是,您可以覆盖开放框架(如 Hibernate)的许多默认功能。但是这个(每个层次结构的表)的问题是您的数据存储在单个表中。并且必须有一列来标识层次结构中哪个数据行属于哪个Class。以上是关于我可以删除 Hibernate 单表继承中的鉴别器列吗?的主要内容,如果未能解决你的问题,请参考以下文章