Overview
在本指南中,您可以学习如何使用 Hibernate ORM 的 MongoDB 扩展在 MongoDB 数据库上运行原生查询。与 Hibernate 查询语言 (HQL) 或 Jakarta Persistence 查询语言 (JPQL) 不同,原生查询允许您使用 MongoDB 查询语言 (MQL) 来指定查询。MQL 是一种专为与 MongoDB 基于文档的模型交互而设计的查询语法。
Hibernate ORM 的 createQuery() 方法不支持某些MongoDB查询功能。createNativeQuery() 方法允许您在MQL中指定数据库查询并绕过 Hibernate ORM 扩展的某些操作限制。
您还可以直接对 MongoClient对象运行查询,以扩展查询功能。
样本数据
The examples in this guide use the Movie entity, which represents the sample_mflix.movies collection from the Atlas sample datasets. The Movie entity has the following definition:
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 int runtime; private List<String> cast; private List<String> directors;
public Movie(String title, String plot, int year, int runtime, List<String> cast, List<String> directors) { this.title = title; this.plot = plot; this.year = year; this.runtime = runtime; 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 int getRuntime() { return runtime; }
public void setRuntime(int runtime) { this.runtime = runtime; }
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 Extension for Hibernate ORM 与此 MongoDB 示例集合交互的 Java 应用程序,请参阅 入门 教程。
注意
持久性上下文
To enable Hibernate ORM to interact with the database, you must run operations inside a persistence context by using a Hibernate Session or a Jakarta Persistence EntityManager. The examples in this guide use a Session. To learn more about persistence contexts, see the Transactions and Sessions guide.
运行原生查询
要运行原生 MongoDB 查询,请指定 MongoDB 查询语言 (MQL) 声明,其中包括要查询的集合以及聚合管道中的查询条件。
MQL声明采用以下格式:
String mqlSyntax = """{ aggregate: "<collection to query>", pipeline: [ <aggregation pipeline stages> ]}""";重要
$ 项目阶段要求
您的 MQL 声明中的聚合管道必须包含一个 $project 阶段。要将查询文档作为实体实例返回,您必须在 $project 阶段中指定每个实体字段,包括 _id 字段。Hibernate ORM 扩展会返回您投影的字段,并在您省略 _id 时将其抑制。省略 _id 的实体绑定查询可能会因 Unknown column label [_id] 而失败,因为实体标识符映射到 _id。要保持标识符可用,请投影 _id: 1。
然后,将 MQL 声明传入 createNativeQuery() 方法。
您可以使用原生查询来执行以下操作:
重要
需要扩展 JSON 语法
您必须以 Extended JSON v2 格式写入原生查询。Hibernate ORM 扩展不支持原生查询中的单引号或 MongoDB Server shell 正则表达式语法 (/pattern/)。示例:要使用正则表达式进行查询,请使用 $regularExpression Extended JSON 类型,而不是 /pattern/。
对文档字段进行过滤和排序
This example runs a native query on the sample_mflix.movies collection by passing an MQL statement to the createNativeQuery() method. The code specifies the following aggregation pipeline stages:
$match:过滤器用于标题字段值为 的文档"The Parent Trap"$sort:按匹配文档的year字段降序排序$project:返回Movie实体中定义的每个文档字段
String nativeQuery = """{ aggregate: "movies", pipeline: [ { $match: { title: { $eq: "The Parent Trap" } } }, { $sort: { year: -1 } }, { $project: { _id: 1, title: 1, plot: 1, year: 1, runtime: 1, cast: 1 } } ]}""";
var results = session.createNativeQuery(nativeQuery, Movie.class) .getResultList();
for (Movie movie : results) { System.out.println("Title: " + movie.getTitle() + ", Year: " + movie.getYear());}使用算术操作符
注意
原生查询最适合 Hibernate ORM 扩展不支持的算术操作。要了解 Hibernate ORM 扩展支持哪些算术操作符,请参阅“功能兼容性”页面中的查询支持部分。
The following example runs a native query on the sample_mflix.movies collection that performs the following actions:
指定一个
$match阶段,以匹配year值大于2000且runtime字段存在的文档指定
$addFields阶段以添加名为runtimeHours的新字段对于新的
runtimeHours字段,使用$divide算术操作符将runtime值从分钟转换为小时指定一个
$project阶段以返回Movie实体中定义的每个文档字段,包括_id字段打印每个已更新文档的
title值
String nativeQuery = """{ aggregate: "movies", pipeline: [ { $match: { year: { $gt: 2000 }, runtime: { $exists: true } } }, { $addFields: { runtimeHours: { $divide: [ "$runtime", 60 ] } }}, { $project: { _id: 1, title: 1, plot: 1, year: 1, runtime: 1, cast: 1, runtimeHours: 1 }} ]}""";
var results = session.createNativeQuery(nativeQuery, Movie.class) .getResultList();
for (Movie result : results) { System.out.println("Added field to movie: " + result.getTitle());}运行MongoDB搜索查询
您可以运行原生查询来对数据库执行MongoDB 搜索查询,这是对数据的细粒度文本搜索。这些查询提供高级搜索功能,例如匹配文本短语、对结果的相关性进行评分以及突出显示匹配项。
重要
您无法在ACID 事务中运行MongoDB 搜索查询。
要指定搜索查询,请创建涵盖要查询的字段的搜索索引。然后,将聚合管道传递给包含 $search 或 $searchMeta 阶段的 createNativeQuery() 方法。
This example runs a Search query by passing the $search pipeline stage to the createNativeQuery() method. The code performs the following actions:
指定涵盖
plot字段的搜索索引。确保将<indexName>占位符替换为您的搜索索引名称。查询
plot值包含字符串"whirlwind romance"且字符串之间的单词数不超过3个的文档指定一个
$project阶段以返回Movie实体中定义的每个文档字段,包括_id字段打印匹配文档的
title和plot值
String nativeQuery = """{ aggregate: "movies", pipeline: [ { $search: { index: "<indexName>", phrase: { path: "plot", query: "whirlwind romance", slop: 3 } } }, { $project: { _id: 1, title: 1, plot: 1, year: 1, runtime: 1, cast: 1 } } ]}""";
var results = session.createNativeQuery(nativeQuery, Movie.class) .getResultList();
for (Movie result : results) { System.out.println("Title: " + result.getTitle() + ", Plot: " + result.getPlot());}在原生查询中使用参数
您可以使用命名参数 (:name) 或序号参数 (?1) 在 MQL 声明中指定值。以下示例展示了如何将 title 参数值作为命名参数或序号参数提供,然后使用 setParameter() 提供参数值:
String nativeQuery = """{ aggregate: "movies", pipeline: [ { $match: { title: { $eq: :movieTitle } } }, { $sort: { year: -1 } }, { $project: { _id: 1, title: 1, plot: 1, year: 1, runtime: 1, cast: 1 } } ]}""";
var results = session.createNativeQuery(nativeQuery, Movie.class) .setParameter("movieTitle", "The Parent Trap") .getResultList();
for (Movie movie : results) { System.out.println("Title: " + movie.getTitle() + ", Year: " + movie.getYear());}String nativeQuery = """{ aggregate: "movies", pipeline: [ { $match: { title: { $eq: ?1 } } }, { $sort: { year: -1 } }, { $project: { _id: 1, title: 1, plot: 1, year: 1, runtime: 1, cast: 1 } } ]}""";
var results = session.createNativeQuery(nativeQuery, Movie.class) .setParameter(1, "The Parent Trap") .getResultList();
for (Movie movie : results) { System.out.println("Title: " + movie.getTitle() + ", Year: " + movie.getYear());}运行 MongoClient 操作
如果要运行createQuery() 方法和 createNativeQuery() 方法都不支持的数据库操作,可以直接在Java应用程序中操作 MongoClient对象。使用 MongoClient 时,您可以访问 MongoDB Java Sync Driver 的功能。
要学习如何使用Java驱动程序与MongoDB交互,请参阅MongoDB Java 驱动程序文档。
使用 MongoClient 创建索引
您无法使用 Hibernate ORM 扩展在集合上创建索引,但可以实例化 MongoClient 并使用Java驱动程序的 createIndex() 方法。以下代码在 sample_mflix.movies集合上创建 title字段索引:
// Replace the <connection URI> placeholder with your MongoDB connection URIString uri = "<connection URI>";MongoClient mongoClient = MongoClients.create(uri);MongoDatabase db = mongoClient.getDatabase("sample_mflix");MongoCollection<Document> collection = db.getCollection("movies");
String indexResult = collection.createIndex(Indexes.ascending("title"));System.out.println(String.format("Index created: %s", indexResult));要学习有关如何使用Java驱动程序创建索引的更多信息,请参阅Java驱动程序文档中的索引指南。
更多信息
要进一步学习;了解本指南中讨论的查询语言,请参阅以下资源:
Hibernate ORM 文档中的 Hibernate 查询语言指南