Overview
在本指南中,您可以学习如何创建表示 MongoDB 集合的 Hibernate ORM 实体。实体是定义数据结构的Java类。使用 Hibernate ORM 扩展时,您可以将每个实体映射到MongoDB集合,并使用这些实体与集合的文档进行交互。
MongoDB BSON字段
MongoDB以称为BSON的二进制表示形式组织和存储文档,从而支持灵活数据处理。本节介绍 Hibernate ORM 扩展对BSON字段的支持,您可以将其包含在实体中。
下表描述了支持的BSON字段类型以及可在 Hibernate ORM 实体中使用的 Hibernate ORM 扩展等效项:
BSON字段类型 | 扩展字段类型 | BSON描述 |
|---|---|---|
|
| 表示空值或数据缺失。 |
|
| 存储子类型为 0 的二进制数据。 |
|
| 存储 UTF-8 编码的字符串值。 |
|
| 存储 32 位有符号整数。 |
|
| 存储 64 位有符号整数。 |
|
| 存储浮点值。 |
|
| 存储 |
|
| 存储 28 位十进制值。 |
|
| 存储MongoDB用主节点 (primary node in the replica set)键的唯一 12 字节标识符。 |
|
| 将日期和时间存储为自 Unix 纪元以来的毫秒数。 |
|
| 存储嵌入式文档,并根据各自的类型映射字段值。 |
|
| 存储大量值,其中的元素根据各自的类型进行映射。字符数组需要设置 |
注意
不支持 @JdbcTypeCode 注解
Hibernate ORM 扩展不支持@org.hibernate.annotations.JdbcTypeCode 注解,如果您使用此注解覆盖字段的类型映射,则会引发异常。
不支持的字段类型
当 Hibernate ORM 没有其他映射到该类型时,它会将实现 java.io.Serializable 的任何Java类型序列化为二进制数据。为了防止您的数据以这种格式存储,Hibernate ORM 扩展会拒绝以下类型,并在应用程序启动时引发异常。此检查适用于主节点 (primary node in the replica set)键、普通字段、可嵌入属性和集合元素。
下表描述了不支持的字段类型及其支持的替代项:
category | 不支持的类型 | 支持的替代方案 |
|---|---|---|
日期和时间 |
| 使用 |
BSON Values |
| 对于二进制数据使用 |
BSON 文档 |
| 使用 |
定义实体
要创建表示MongoDB集合的实体,请在项目的基础包目录中创建一个新的Java文件,并将实体类添加到新文件中。在实体类中,指定要存储的字段和集合名称。
@jakarta.persistence.Table 注释的 name 元素表示MongoDB集合名称。您还可以设立可选的 schema 元素作为集合名称的前缀,如本指南的模式限定符部分所述。使用以下语法定义实体:
public class <EntityName> { // Specify your primary key field here private <field type> <field name>; // Include additional fields here private <field type> <field name>; // Parameterized constructor public <EntityName>(<parameters>) { // Initialize fields here } // Default constructor public <EntityName>() { } // Getter and setter methods public <field type> get<FieldName>() { return <field name>; } public void set<FieldName>(<field type> <field name>) { this.<field name> = <field name>; } }
要使用实体,您可以在应用程序文件中查询实体。要学习有关 Hibernate ORM 扩展中的 CRUD 操作的更多信息,请参阅 执行 CRUD 操作 指南。
重要
主键字段名称必须是_id
MongoDB需要主键字段映射到 _id字段。您可以使用 @Column 注释或 orm.xml 覆盖来显式设立@Id 字段的列名称。如果将此名称设立为 _id 以外的任何名称,则 Hibernate ORM 扩展会在引导时抛出 FeatureNotSupportedException。要解决此错误,删除@Column 注解或将其名称设立为 _id。
此验证不适应用传统 Hibernate Mapping (HBM) XML 映射。如果您使用 HBM XML 映射实体,则 Hibernate ORM 扩展会以静默方式将标识符列重命名为 _id,而不是引发异常。
模式限定符
@Table 注解接受可选的 schema 元素,该元素作为集合名称的前缀。当您设立这两个元素时,Hibernate ORM 扩展会将实体映射到名为 <schema name>.<collection name> 的集合。
模式限定符仅更改集合的名称。模式不是单独的MongoDB 数据库。每个模式限定的集合都驻留在 SessionFactory实例连接到的数据库中。由于这些集合股票一个数据库,因此一个ACID 事务可以跨越多个模式。
使用以下语法应用模式限定符:
public class <EntityName> { // Define your fields, constructors, and methods here }
注意
模式限定符限制
Hibernate ORM 扩展会在引导时拒绝 @Table 注解的 catalog 元素和 hibernate.default_catalog 配置属性。如果您的应用程序必须访问权限多个MongoDB数据库,请为每个数据库创建一个单独的 SessionFactory实例。
Hibernate ORM 扩展在引导时拒绝表名称或模式名称中的点 (.)。此限制适用于主节点 (primary node in the replica set)、从节点(secondary node from replica set)、联接和集合表名称,以及由 schema 属性或 hibernate.default_schema 配置属性设立的模式名称。
例子
此示例Movie.java 实体类定义了一个 Movie 实体,其中包含以下信息:
@Entity将该类标记为 Hibernate ORM 实体的注解@Table注释,用于将实体映射到Atlas示例数据集中的movies集合@Id和@ObjectIdGenerator注解,用于将id字段指定为主主键并配置自动ObjectId生成表示电影数据的私有字段
用于实体实例化的默认构造函数和参数化构造函数
提供对实体字段访问权限权限的 Getter 和 setter 方法
package org.example; import com.mongodb.hibernate.annotations.ObjectIdGenerator; import org.bson.types.ObjectId; import java.util.List; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.Table; public class Movie { private ObjectId id; private String title; private String plot; private int year; private List<String> cast; private List<String> directors; public Movie(String title, String plot, int year, List<String> cast, List<String> directors) { this.title = title; this.plot = plot; this.year = year; this.cast = cast; this.directors = directors; } public Movie() { } public ObjectId getId() { return id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getPlot() { return plot; } public void setPlot(String plot) { this.plot = plot; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } public List<String> getCast() { return cast; } public void setCast(List<String> cast) { this.cast = cast; } public List<String> getDirectors() { return directors; } public void setDirectors(List<String> directors) { this.directors = directors; } }
提示
要了解有关实体类定义中使用的字段的更多信息,请参阅本指南中MongoDB BSON字段部分。
序列生成的 ID
Hibernate ORM 扩展可以自动为实体分配递增的数字 ID。如果ACID 事务回滚或应用程序未使用其保留的所有 ID,则某些数字可能会丢失。
Hibernate ORM 扩展支持序列生成的 short、int 和 long 类型以及装箱 Short、Integer 和 Long 类型的 ID。
将序列生成的 ID 添加到实体
您可以使用 @GeneratedValue 和 @SequenceGenerator 注释将序列生成的 ID 添加到实体。以下示例在 movies_SEQ 计数器中以 50 区块的形式保留 ID:
private Long id;
注解控制 Hibernate ORM 扩展如何创建ID:
@GeneratedValue指示 Hibernate ORM 扩展创建id字段的值。其strategy和generator属性定义了扩展如何创建ID:strategy:AUTO或SEQUENCE。对于AUTO,Hibernate ORM 扩展会使用序列,而您不必声明@SequenceGenerator。对于SEQUENCE,Hibernate ORM 扩展使用由generator属性命名的序列生成器。generator:要使用的@SequenceGenerator的名称。如果省略此属性,Hibernate ORM 扩展将按照实体的表命名序列,例如movies表的movies_SEQ。
@SequenceGenerator定义序列生成器:name:@GeneratedValuegenerator属性引用的名称。sequenceName:相反文档的名称。如果省略它,Hibernate ORM 扩展将使用生成器的name。initialValue:第一个生成的ID的起始值。默认为1。allocationSize:每个区块中保留的 ID 数量。
ID 的存储位置
Hibernate ORM 扩展将计数器文档存储在名为 hibernate_sequences 的固定集合中。每个序列使用一个计数器文档,其中包含以下字段:
_id是序列名称。next_value是 Hibernate ORM 扩展前进以生成下一个 ID 的计数器值。它必须与序列生成器的initialValue匹配,默认为1。increment是一次保留的 ID 数量。它必须与序列生成器的allocationSize值匹配。否则,SessionFactory将无法启动。
Hibernate ORM 扩展以区块的形式获取 ID。这减少了它必须联系数据库的次数。每个同时发出的请求都会收到不同的ID。
重要
抗辩文件要求
next_value 和 increment 都必须是 64 位整数 (BSON Int64 )。缺少任一字段或将值存储为 32 位整数的计数器文档无法分配 ID。如果禁用了模式管理,则必须先为每个序列创建计数器文档,然后应用程序才能分配 ID。
启用模式管理
模式管理是 Hibernate ORM 创建实体所需数据库对象的方式。 Hibernate ORM 扩展使用它来为序列生成的 ID 创建计数器文档。
要启用模式管理,请在 hibernate.properties 或 persistence.xml文件中设立jakarta.persistence.schema-generation.database.action属性。使用以下值之一:
create:创建计数器文档并在SessionFactory关闭时保留这些文档。create-drop: Creates the counter documents when theSessionFactoryopens and drops them when it closes.
以下示例使用 create:
jakarta.persistence.schema-generation.database.action=create
禁用模式管理和手动计数器设置
如果您希望关闭模式管理,则可以为序列生成的 ID 手动创建计数器文档。设置以下属性:
jakarta.persistence.schema-generation.database.action=none
然后在 hibernate_sequences集合中为每个序列创建一个计数器文档。计数器文档的 _id 必须与序列生成器的 sequenceName 匹配。
以下示例使用MongoDB Java驾驶员将 movies_SEQ 计数器文档插入到指定数据库的 hibernate_sequences集合中:
// Replace the placeholders with your connection string and database name. MongoClient client = MongoClients.create("<connection string>"); MongoDatabase database = client.getDatabase("<database name>"); database.getCollection("hibernate_sequences").insertOne( new Document("_id", "movies_SEQ") .append("next_value", new BsonInt64(1)) .append("increment", new BsonInt64(50)));
插入操作会创建所示的相对文档。将 next_value 设置为生成器的 initialValue,将 increment 设置为其 allocationSize。
保存实体并读取其生成的ID
以下示例保存一个新的 Movie 实体并打印其生成的ID。 ID取决于序列的点。对于从 1 开始的新计数器,输出为:
var movie = new Movie(); movie.setTitle("The Matrix"); session.persist(movie); System.out.println("Movie created with ID: " + movie.getId());
不支持的配置
Hibernate ORM 扩展不支持以下与序列相关的策略、类型和配置:
IDENTITY和TABLE生成策略BigInteger和BigDecimal生成的标识符类型包含
.字符的序列名称或模式由目录限定的序列名称,例如由三部分组成的名称或
catalog属性顺序
options@GenericGenerator和@TableGenerator生成器、使用@IdGeneratorType注解的自定义生成器以及命名传统生成器,例如identity、table或increment将实体映射到
hibernate_sequences集合insert ... select需要对生成的 ID 进行内联分配的语句
复合主键
要为一个实体设置多个字段的键,请定义复合主键。创建一个普通的 @Embeddable 类或记录来保存关键组件。然后,使用 @jakarta.persistence.EmbeddedId 注释来注释实体的标识符字段。
以下 BookId 嵌入式对象定义了一个由 publisherId 组件和 bookNo 组件组成的键:
public record BookId(long publisherId, long bookNo) {}
以下 Book 实体使用 BookId 作为主键:
public class Book { private BookId id; private String title; public Book() { } public Book(BookId id, String title) { this.id = id; this.title = title; } // Getter and setter methods }
在持久保存实体之前,必须在应用程序中分配复合键值。 Hibernate ORM 扩展不会生成组合键值。
复合密钥存储
Hibernate ORM 扩展将组合键存储为 _id 子文档。前面的 Book 实体生成以下格式的文档:
{ "_id": { "bookNo": 2, "publisherId": 10 }, "title": "My Book" }
Hibernate ORM 扩展按组件名称的字母顺序(而不是按照您声明它们的顺序)对 _id 子文档的组件进行排序。无论将可嵌入对象声明为类还是记录,此顺序都是相同的。
重要
组件顺序影响文档匹配
MongoDB按字段顺序比较子文档,因此以不同顺序包含相同组件的两个 _id 值不匹配。由于 Hibernate ORM 扩展始终以相同的顺序写入组件,因此仅当您也在 Hibernate ORM 扩展之外读取或写入这些文档时(例如通过MongoDB Java驾驶员),此行为才会影响您。
组合键限制
如果您通过以下任一方式声明组合键,则 Hibernate ORM 扩展会在应用程序启动时引发 FeatureNotSupportedException:
非聚合标识符,使用
@jakarta.persistence.IdClass注解或多个@Id属性进行声明。请改用@EmbeddedId来声明密钥。一个可作为标识符嵌入的
@Struct聚合。请改用普通的@Embeddable。非基本值的组件,例如嵌套的
@Embeddable或集合。组合键的每个组件都必须是基本值。标识符中的关联,包括使用
@jakarta.persistence.MapsId注解的派生标识。
您也不能使用排序运算操作符(例如 > 或 <)来比较整个复合标识符。而是比较单个组件。
嵌入式数据
Hibernate ORM 扩展通过 Hibernate ORM @Embeddable 注释支持嵌入式文档。使用嵌入式文档,您可以在MongoDB文档中创建一对多、多对一和一对一关系。这种格式非常适合表示经常一起访问的数据。
要表示嵌入式文档,请在类上使用 @Struct 和 @Embeddable 注解来创建 @Struct 聚合嵌入式文档。然后,将可嵌入类型作为字段包含在父实体中。Hibernate ORM 扩展支持嵌入单个对象、数组和可嵌入对象的集合。
提示
要了解有关 @Struct 聚合可嵌入项的更多信息,请参阅 Hibernate ORM 文档中的 @Struct 聚合可嵌入项映射。
一对一关系
A One-to-One relationship is when a record in one database is associated with exactly one record in another database. In MongoDB, you can create a collection with an embedded document field to model a One-to-One relationship. The Hibernate ORM extension allows you to create embedded document fields by using @Struct aggregate embeddables.
The example defines a field with a @Struct aggregate embeddable type in an entity similar to the Define an Entity example in this guide. The sample Movie.java entity class includes the following information:
@Entity和@Table注解,用于定义实体并将其映射到movies集合@Id以及将id字段指定为主键的@ObjectIdGenerator注解表示电影标题的字符串字段
@Struct聚合表示电影奖项和工作室信息的可嵌入式字段
The following example represents a One-to-One relationship because each Movie entity is associated with one Awards embeddable and one Studio embeddable:
public class Movie { private ObjectId id; private String title; private Awards awards; private Studio studio; public Movie(String title, Awards awards, Studio studio) { this.title = title; this.awards = awards; this.studio = studio; } public Movie() { } // Getter and setter methods }
以下示例代码创建一个 Awards @Struct 聚合可嵌入对象:
public class Awards { private int wins; private int nominations; private String text; public Awards(int wins, int nominations, String text) { this.wins = wins; this.nominations = nominations; this.text = text; } public Awards() { } // Getter and setter methods }
以下示例代码创建一个 Studio @Struct 聚合可嵌入对象:
public class Studio { private String name; private String location; private int foundedYear; public Studio(String name, String location, int foundedYear) { this.name = name; this.location = location; this.foundedYear = foundedYear; } public Studio() { } // Getter and setter methods }
一对多关系
一对多关系是指一个数据库中的一条记录与另一个数据库中的多条记录相关联。在MongoDB中,您可以定义一个集合字段来存储嵌入式文档列表,以对一对多关系进行建模。Hibernate ORM 扩展允许您使用 @Struct 聚合可嵌入对象的列表来创建嵌入式文档字段。
The example defines a field that stores a list of @Struct aggregate embeddables in an entity similar to the Define an Entity Example in this guide. The sample Movie.java entity class includes the following information:
@Entity和@Table注解,用于定义实体并将其映射到movies集合@Id以及将id字段指定为主键的@ObjectIdGenerator注解表示电影标题的字符串字段
存储多个
Writer@Struct聚合可嵌入对象的列表字段,表示作者信息
The following example represents a One-to-Many relationship because each Movie entity is associated with multiple Writer embeddables:
public class Movie { private ObjectId id; private String title; private List<Writer> writers; public Movie(String title, List<Writer> writers) { this.title = title; this.writers = writers; } public Movie() { } // Getter and setter methods }
以下示例代码创建一个 Writer @Struct 聚合可嵌入对象:
public class Writer { private String name; public Writer() { } public Writer(String name) { this.name = name; } // Getter and setter methods }
嵌套可嵌入对象
您可以将展平的可嵌入对象嵌套在 @Struct 聚合可嵌入对象中。扁平化嵌入式类是包含 @Embeddable 注解但不包含 @Struct 注解的类。 Hibernate ORM 扩展将展平嵌入式文档的字段存储为父嵌入式文档的字段,而不是单独的嵌套文档。
以下示例代码创建一个 Studio @Struct 聚合可嵌入对象,其中包含 Address 扁平可嵌入对象作为字段:
public class Studio { private String name; private Address address; public Studio() { } public Studio(String name, Address address) { this.name = name; this.address = address; } // Getter and setter methods }
以下示例代码创建 Address 展平的可嵌入对象。该类省略了 @Struct 注解:
public class Address { private String city; private String country; public Address() { } public Address(String city, String country) { this.city = city; this.country = country; } // Getter and setter methods }
当您保留包含 Studio字段的实体时,Address 字段将成为 Studio 字段的嵌入式文档的字段。以下示例文档有一个名为 studio 的 Studio字段,用于存储其 Address 扁平化可嵌入对象的字段:
{ "_id": { "$oid": "..." }, "title": "Breathless", "studio": { "name": "Les Films Impéria", "city": "Paris", "country": "France" } }
更多信息
要学习;了解如何将类层次结构映射到MongoDB集合,请参阅映射实体继承层次结构指南。
要学习;了解如何使用实体运行数据库操作,请参阅“与数据交互”部分中的以下指南:
要学习;了解有关 Hibernate ORM 字段的更多信息,请参阅 Hibernate ORM 文档中的映射类型部分。
要学习;了解有关 Hibernate ORM 实体的更多信息,请参阅 Hibernate ORM 文档中的POJO 模型。