Overview
在本指南中,您可以学习如何使用 Hibernate ORM 扩展将 Java 类层次结构映射到 MongoDB 集合。当 Java 子类扩展父类并继承其字段时,这些类就形成了层次结构。Hibernate ORM 提供了继承策略,用于确定扩展如何存储层次结构中的每个类。您选择的策略决定了哪些集合保存您的数据以及生成的文档的形状。
Hibernate ORM 扩展支持以下继承策略:
单表继承:Hibernate ORM 扩展将整个类层次结构存储在一个集合中。
映射超类:非实体超类将其字段贡献给每个子类,而无需拥有自己的集合。
该扩展不支持 JOINED 和 TABLE_PER_CLASS 继承策略。要了解更多信息,请参阅本指南的 不支持的策略 部分。
单表继承
在单表继承策略中,框架将层次结构中的每个类存储在同一个 MongoDB 集合中。要使用此策略,请将 @Inheritance(strategy = InheritanceType.SINGLE_TABLE) 注解应用于根实体,即层次结构中最高级别的父类。
由于集合中包含映射到多个类的文档,因此扩展必须记录每个文档映射到的类。当您保存对象时,扩展会将此信息存储在 discriminator 字段中。当您查询集合时,扩展会读取 discriminator 值以确定要实例化的类。
要指定识别符字段,将 @DiscriminatorColumn 注解应用到根实体。要设置类的分辨器值,应用 @DiscriminatorValue 注解到该类。
以下示例将 Movie 层次结构映射到 movies 集合。Actor 和 Director 子类都从 Movie 继承了 name 字段,但每个子类也添加了自己的字段。每个文档都在 role 鉴别器字段中存储 "actor" 或 "director"。
@Entity@Table(name = "movies")@Inheritance(strategy = InheritanceType.SINGLE_TABLE)@DiscriminatorColumn(name = "role")public abstract class Movie { @Id @ObjectIdGenerator private ObjectId id; private String name;
// Getter and setter methods
}
@Entity@DiscriminatorValue("actor")public class Actor extends Movie { private List<String> awards;
// Getter and setter methods
}
@Entity@DiscriminatorValue("director")public class Director extends Movie { private String studio;
// Getter and setter methods
}当您在 Actor 实例或 Director 实例上调用 persist() 方法时,扩展程序会将相应的文档插入到 movies 集合中。每个文档都包含其自身类定义的字段以及该类的鉴别器值:
{ "_id": ObjectId("..."), "role": "actor", "name": "Andrew Garfield", "awards": ["Golden Globe Award"] }{ "_id": ObjectId("..."), "role": "director", "name": "Greta Gerwig", "studio": "Warner Bros." }前面的示例使用 string 鉴别器,这是默认的鉴别器类型。要更改此类型,请设置 @DiscriminatorColumn 注解的 discriminatorType 元素。将此元素设置为 DiscriminatorType.CHAR 可将鉴别器存储为字符,或设置为 DiscriminatorType.INTEGER 可将鉴别器存储为整数。
重要
字符和整数鉴别器需要显式值
对于字符或整数鉴别器,Hibernate 不会将类的鉴别器值默认设置为类名。如果将 discriminatorType 设置为 DiscriminatorType.CHAR 或 DiscriminatorType.INTEGER,则必须在根实体和每个子类中添加 @DiscriminatorValue 注解,即使您从未直接实例化根实体也是如此。
查询单表层次结构
movies 集合包含 Movie 层次结构中每个类的文档。当您查询这些类之一时,Hibernate ORM 扩展会过滤鉴别器字段,以便查询仅返回该类的文档。此过滤器取决于您查询的类:
如果您查询子类,扩展程序会匹配鉴别器等于该子类鉴别器值的文档。在前面的示例中查询
Actor仅匹配role为"actor"的文档。如果您查询根类,扩展程序不会添加过滤器。该查询会读取集合中的每个文档,并使用每个文档的鉴别器值来返回正确类的对象。在前面的示例中查询
Movie会返回Actor和Director文档。如果集合包含一个鉴别器值未映射到层次结构中类的文档,则查询会抛出HibernateException。
要了解有关查询实体的更多信息,请参阅指定查询指南。
映射超类
映射超类与子类共享字段映射,但其本身不会成为实体。要创建映射超类,请将 @MappedSuperclass 注解应用于超类。由于映射超类不是实体,Hibernate ORM 扩展不会为其创建集合,也无法直接对其进行查询。
每个扩展映射超类的子类都映射到其自己的集合。在每个文档中,扩展将继承的字段和子类自己的字段存储为顶级字段。扩展不会嵌套继承的字段,也不会单独存储它们。
以下示例定义了一个 MovieDetails 映射超类,它为 Genre 实体和 Language 实体提供标识符和名称映射:
@MappedSuperclasspublic abstract class MovieDetails { @Id @ObjectIdGenerator private ObjectId id; private String name;
// Getter and setter methods
}
@Entity@Table(name = "genres")public class Genre extends MovieDetails { private String description;
// Getter and setter methods
}
@Entity@Table(name = "languages")public class Language extends MovieDetails { private String code;
// Getter and setter methods
}在前面的示例中,@MappedSuperclass 注解允许 MovieDetails 定义 id 和 name 映射,而无需成为实体。Genre 和 Language 上的 @Entity 和 @Table 注解随后将每个子类映射到其自己的集合。
因此,该扩展不会为 MovieDetails 创建集合。相反,该扩展将 Genre 文档存储在 genres 集合中,将 Language 文档存储在 languages 集合中。除了其对应类定义的字段外,每个文档还包含 MovieDetails 映射的 _id 和 name 字段:
{ "_id": ObjectId("..."), "description": "Serious, narrative-driven stories.", "name": "Drama" }{ "_id": ObjectId("..."), "code": "en", "name": "English" }不支持的策略
Hibernate ORM 扩展不支持以下 Hibernate ORM 继承策略,这些策略将单个类层次结构存储在多个表中:
InheritanceType.JOINED:将每个类存储在自己的表中,并连接这些表以重构实例InheritanceType.TABLE_PER_CLASS:将每个具体类存储在各自的表中,并组合这些表以查询层次结构
如果您使用上述任何策略映射类,扩展程序将抛出 FeatureNotSupportedException。
重要
启动时发生错误
Hibernate ORM 扩展在构建 SessionFactory 实例时会验证继承策略。使用不受支持策略的应用程序无法启动。
更多信息
要了解有关创建实体和映射嵌入式数据的更多信息,请参阅创建实体以表示集合指南。
要了解如何使用实体运行数据库操作,请参阅执行 CRUD 操作和指定查询指南。
要查看 Hibernate ORM 扩展支持的功能列表,请参阅功能兼容性页面。
要学习;了解有关 Hibernate ORM 继承策略的更多信息,请参阅 Hibernate ORM 文档中的继承。