Overview
在本指南中,您可以学习如何创建表示 MongoDB 集合的 Hibernate ORM 实体。实体是定义数据结构的Java类。使用 Hibernate ORM 扩展时,您可以将每个实体映射到MongoDB集合,并使用这些实体与集合的文档进行交互。
MongoDB BSON字段
MongoDB以称为BSON的二进制表示形式组织和存储文档,从而支持灵活数据处理。本节介绍 Hibernate ORM 扩展对BSON字段的支持,您可以将其包含在实体中。
下表描述了支持的BSON字段类型以及可在 Hibernate ORM 实体中使用的 Hibernate ORM 扩展等效项:
BSON字段类型 | 扩展字段类型 | BSON描述 |
|---|---|---|
|
| 表示空值或数据缺失。 |
|
| 存储子类型为 0 的二进制数据,或子类型为 4 的 UUID 值。 |
|
| 存储 UTF-8 编码的 string 值。 |
|
| 存储 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 扩展会拒绝以下类型,并在您的应用程序启动时抛出异常。此检查应用于主键、普通字段、可嵌入属性和集合元素。
下表介绍了不支持的字段类型及其支持的替代方案:
category | 不支持的类型 | 支持的替代方案 |
|---|---|---|
日期和时间 |
| 使用 |
BSON Values |
| 使用 |
BSON 文档 |
| 使用 |
定义实体
要创建一个表示 MongoDB 集合的实体,请在项目的基本包目录中创建一个新的 Java 文件,并将您的实体类添加到该新文件中。在您的实体类中,指定要存储的字段和集合名称。
@jakarta.persistence.Table 注解的 name 元素表示您的 MongoDB 集合名称。您还可以设置可选的 schema 元素来为集合名称添加前缀,如本指南的 模式限定符 部分所述。使用以下语法定义实体:
@Entity@Table(name = "<collection name>")public class <EntityName> { @Id // 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 实例连接到的数据库中。由于这些集合共享一个数据库,因此一个事务可以跨越多个模式。
使用以下语法应用模式限定符:
@Entity@Table(schema = "<schema name>", name = "<collection name>")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;
@Entity@Table(name = "movies")public class Movie { @Id @ObjectIdGenerator 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 分配给您的实体。如果事务被回滚,或者应用程序未使用其预留的所有 ID,则某些 ID 可能会缺失。
Hibernate ORM 扩展支持 short、int 和 long 类型以及装箱的 Short、Integer 和 Long 类型的序列生成 ID。
将序列生成的 ID 添加到您的实体
您可以使用 @GeneratedValue 和 @SequenceGenerator 注解将序列生成的 ID 添加到您的实体中。以下示例从 movies_SEQ 计数器中保留 50 块的 ID:
@Id@GeneratedValue( strategy = GenerationType.SEQUENCE, generator = "movies_SEQ")@SequenceGenerator( name = "movies_SEQ", sequenceName = "movies_SEQ", allocationSize = 50)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 创建计数器文档。
要启用模式管理,请在您的 jakarta.persistence.schema-generation.database.action 或 hibernate.properties 文件中设置 persistence.xml 属性。使用以下值之一:
create:创建计数器文档并在SessionFactory关闭时保留它们。create-drop:在SessionFactory打开时创建计数器文档,并在其关闭时将其删除。
以下示例使用 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 的声明
UUID 生成的 ID
UUID 是一个 标准化 16 字节的唯一 ObjectId,Hibernate ORM 扩展可以为您的实体生成该标识符。要生成 UUID 标识符,请使用 @UuidGenerator 注解标注实体的 @Id 字段,或将 @GeneratedValue 注解的 strategy 元素设置为 UUID。以下示例定义了一个具有 UUID 生成 ID 的实体:
package org.example;
import jakarta.persistence.Entity;import jakarta.persistence.Id;import jakarta.persistence.Table;import java.util.UUID;import org.hibernate.annotations.UuidGenerator;
@Entity@Table(name = "items")public class UuidItem {
@Id @UuidGenerator // or: @GeneratedValue(strategy = GenerationType.UUID) private UUID id;
private String title;
public UuidItem(String title) { this.title = title; }
public UuidItem() { }
// Getter and setter methods}UUID 的存储位置
Hibernate ORM 扩展将 java.util.UUID 值存储为具有子类型 4 的 BSON Binary 值。以下示例文档具有一个带有生成的 UUID 的 _id 字段:
{ "_id": { "$binary": { "base64": "FXvL4sBrQoueEByraiJUmA==", "subType": "04" } }, "title": "The Matrix"}如果 @Id 字段是 String,Hibernate ORM 扩展会将生成的 UUID 存储为 BSON String,而不是 BSON Binary 值。
要了解有关BSON字段类型映射的更多信息,请参阅本指南中的MongoDB BSON字段部分。
保存实体并读取其生成的 UUID
以下示例保存了一个新的 UuidItem 实体并打印其生成的 ID:
var item = new UuidItem();item.setTitle("The Matrix");session.persist(item);System.out.println("UuidItem created with ID: " + item.getId());UUID 限制
当您使用 UUID 生成的 ID 时,Hibernate ORM 扩展具有以下限制:
Hibernate ORM 扩展不支持旧版标识符生成器名称
uuid、uuid.hex、uuid2和guid。如果您指定其中一个名称,Hibernate ORM 扩展会在您的应用程序启动时抛出FeatureNotSupportedException。请改用@UuidGenerator或@GeneratedValue(strategy = GenerationType.UUID)。如果您在持久化实体之前分配
@UuidGenerator@Id字段的值,Hibernate ORM 扩展会抛出jakarta.persistence.EntityExistsException。要自行分配 UUID 值,请从@Id字段中删除生成器注解。
复合主键
若要基于多个字段对实体进行键控,请定义复合主键。创建一个包含键组件的普通 @Embeddable 类或记录。然后,使用 @jakarta.persistence.EmbeddedId 注解标注实体的标识符字段。
以下 BookId 可嵌入对象定义了一个由 publisherId 组件和 bookNo 组件组成的键:
@Embeddablepublic record BookId(long publisherId, long bookNo) {}以下 Book 实体使用 BookId 作为其主键:
@Entity(name = "Book")@Table(name = "books")public class Book { @EmbeddedId 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:
@Entity@Table(name = "movies")public class Movie {
@Id @ObjectIdGenerator 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 聚合可嵌入对象:
@Embeddable@Struct(name = "Awards")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 聚合可嵌入对象:
@Embeddable@Struct(name = "Studio")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:
@Entity@Table(name = "movies")public class Movie { @Id @ObjectIdGenerator @Column(name = "_id") 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 聚合可嵌入对象:
@Embeddable@Struct(name = "Writer")public class Writer { private String name;
public Writer() { }
public Writer(String name) { this.name = name; }
// Getter and setter methods
}嵌套可嵌入对象
您可以将一个 **flattened embeddable** 嵌套在 @Struct聚合 embeddable 中。flattened embeddable 是一个包含 @Embeddable 注解但不包含 @Struct 注解的类。Hibernate ORM 扩展将 flattened embeddable 的字段存储为父嵌入式文档的字段,而不是作为单独的嵌套文档。
以下示例代码创建了一个 Studio @Struct 聚合可嵌入对象,其中包含一个 Address 扁平化可嵌入对象作为字段:
@Embeddable@Struct(name = "Studio")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 注解:
@Embeddablepublic 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 模型。