Hibernate 技巧——如何将继承层次结构映射到一个表
1. 简介
继承是 Java 中的关键概念之一。因此,大多数领域模型都使用它也就不足为奇了。但不幸的是,这个概念在关系数据库中并不存在,您需要找到一种方法将继承层次结构映射到关系表模型。
JPA 和 Hibernate 支持不同的策略,将继承层次结构映射到各种表模型。让我们看一下我的新书Hibernate Tips – 70 多种常见Hibernate 问题的解决方案的一章,其中我解释了SingleTable策略。它将继承层次结构的所有类映射到同一个数据库表。
2. Hibernate 技巧——如何将继承层次结构映射到一个表
2.1. 问题
我的数据库包含一个表,我想将其映射到实体的继承层次结构。如何定义这样的映射?
2.2. 解决方案
JPA 和 Hibernate 支持不同的继承策略,允许您将实体映射到不同的表结构。SingleTable策略就是其中之一,它将实体的继承层次结构映射到单个数据库表。
在我解释SingleTable策略的细节之前,我们先看一下实体模型。Authors 可以编写不同类型的Publication ,例如Book和BlogPost。Publication类是Book和BlogPost类的超类。
SingleTable策略将继承层次结构的三个实体映射到*publication *表。
如果要使用此继承策略,则需要使用*@Inheritance对超类进行注解,并提供InheritanceType.SINGLE_TABLE作为strategy *属性的值。
您还可以使用*@DiscriminatorColumn对超类进行注解,以定义鉴别器值的名称。Hibernate 使用这个值来确定它必须将数据库记录映射到的实体。如果您没有定义鉴别器列,就像我在下面的代码片段中所做的那样,Hibernate 和所有其他 JPA 实现都使用列DTYPE*。
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Publication {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@Version
private int version;
private String title;
private LocalDate publishingDate;
@ManyToMany
@JoinTable(
name="PublicationAuthor",
joinColumns={@JoinColumn(name="publicationId", referencedColumnName="id")},
inverseJoinColumns={@JoinColumn(name="authorId", referencedColumnName="id")})
private Set<Author> authors = new HashSet<Author>();
...
}
子类需要扩展超类,并且您需要使用*@Entity*对它们进行注解。
JPA 规范还建议使用*@DiscriminatorValue*对其进行注解,以定义此实体类的鉴别器值。如果您不提供此注解,您的 JPA 实现会生成一个鉴别器值。
但是 JPA 规范没有定义如何生成鉴别器值,并且您的应用程序可能无法移植到其他 JPA 实现。Hibernate 使用简单的实体名称作为鉴别器。
@Entity
@DiscriminatorValue("Book")
public class Book extends Publication {
private int numPages;
...
}
如果要选择特定实体、执行多态查询或遍历多态关联,SingleTable策略不需要 Hibernate 生成任何复杂查询。
Author a = em.find(Author.class, 1L);
List<Publication> publications = a.getPublications();
所有实体都存储在同一个表中,Hibernate 可以从那里选择它们而无需额外的JOIN子句。
15:41:28,379 DEBUG [org.hibernate.SQL] -
select
author0_.id as id1_0_0_,
author0_.firstName as firstNam2_0_0_,
author0_.lastName as lastName3_0_0_,
author0_.version as version4_0_0_
from
Author author0_
where
author0_.id=?
15:41:28,384 DEBUG [org.hibernate.SQL] -
select
publicatio0_.authorId as authorId2_2_0_,
publicatio0_.publicationId as publicat1_2_0_,
publicatio1_.id as id2_1_1_,
publicatio1_.publishingDate as publishi3_1_1_,
publicatio1_.title as title4_1_1_,
publicatio1_.version as version5_1_1_,
publicatio1_.numPages as numPages6_1_1_,
publicatio1_.url as url7_1_1_,
publicatio1_.DTYPE as DTYPE1_1_1_
from
PublicationAuthor publicatio0_
inner join
Publication publicatio1_
on publicatio0_.publicationId=publicatio1_.id
where
publicatio0_.authorId=?