Overview
在本指南中,您可以学习;了解如何使用MongoDB Extension for Hibernate ORM 来指定数据库查询。
您可以通过创建查询过滤来优化查询返回的文档设立。查询过滤是一个表达式,用于指定MongoDB在读取或写入操作中用于匹配文档的搜索条件。要创建MongoDB查询筛选器,请使用 Hibernate Query Language (HQL) 或 Jakarta Persistence Query Language (JPQL) 语句。
样本数据
本指南中的示例使用Movie 实体,它表示Atlas示例数据集中的sample_mflix.movies 集合。Movie 实体具有以下定义:
import com.mongodb.hibernate.annotations.ObjectIdGenerator; import org.bson.types.ObjectId; 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 String[] cast; public Movie(String title, String plot, int year, String[] cast) { this.title = title; this.plot = plot; this.year = year; this.cast = cast; } 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 String[] getCast() { return cast; } public void setCast(String[] cast) { this.cast = cast; } }
要学习;了解如何创建使用MongoDB Extension for Hibernate ORM 与此MongoDB示例集合交互的Java应用程序,请参阅入门教程。
重要
持久性上下文
启用Hibernate ORM 能够与数据库交互,您必须使用 Hibernate Session 或 Jakarta Persistence EntityManager 在持久性上下文中运行操作。使用 HQL 定义会话查询,使用 JPQL 定义实体管理器查询。
在运行本指南中的示例之前,请确保将持久性上下文和ACID 事务管理代码添加到应用程序中,代码类似于以下代码:
// Replace <persistence unit> with the name of your persistence unit in the persistence.xml file EntityManagerFactory emf = Persistence.createEntityManagerFactory("<persistence unit>"); EntityManager entityManager = entityManagerFactory.createEntityManager(); entityManager.getTransaction().begin(); // ... Perform CRUD operations here entityManager.getTransaction().commit(); entityManager.close(); emf.close;
要使用EntityManager ,您必须创建一个声明持久性单元的persistence.xml 文件。要学习;了解更多信息,请参阅 Hibernate ORM 文档中的使用 JPA 标准 API 的教程。
运行查询
要查询MongoDB数据,请在会话或实体管理器上调用 createQuery() 方法。然后,在 Hibernate Query Language (HQL) 或 Jakarta Persistence Query Language (JPQL)声明中指定匹配条件。
本节介绍如何执行以下查询操作:
检索所有文档
要从集合中检索所有文档,请将基本 select声明传递给 createQuery() 方法。在此声明中,指定表示要查询的集合的实体。
以下示例通过查询 Movie 实体从 sample_mflix.movies集合中检索所有文档的 title 值:
var allDocs = session.createQuery("select title from Movie", String.class) .getResultList(); for (var t : allDocs) { System.out.println("Title: " + t); }
Title: A Corner in Wheat Title: Gertie the Dinosaur Title: The Perils of Pauline Title: Civilization Title: Where Are My Children? ...
var allDocs = entityManager.createQuery("select m.title from Movie m", String.class) .getResultList(); for (var t : allDocs) { System.out.println("Title: " + t); }
Title: A Corner in Wheat Title: Gertie the Dinosaur Title: The Perils of Pauline Title: Civilization Title: Where Are My Children? ...
检索匹配文档
要检索匹配特定条件的文档,请将带有 where 子句的 SELECT声明传递给 createQuery() 方法。在此声明中,指定表示要查询的集合的实体以及匹配条件。
以下示例从 sample_mflix.movies集合中检索 title 值为 "Romeo and Juliet" 的文档:
var matchingDocs = session.createQuery("from Movie where title = :t", Movie.class) .setParameter("t", "Romeo and Juliet") .getResultList(); for (var m : matchingDocs) { System.out.println("Title: " + m.getTitle() + ", Year: " + m.getYear()); }
Title: Romeo and Juliet, Year: 1936 Title: Romeo and Juliet, Year: 1968
var matchingDocs = entityManager.createQuery("select m from Movie m where m.title = :t", Movie.class) .setParameter("t", "Romeo and Juliet") .getResultList(); for (var m : matchingDocs) { System.out.println("Title: " + m.getTitle() + ", Year: " + m.getYear()); }
Title: Romeo and Juliet, Year: 1936 Title: Romeo and Juliet, Year: 1968
Retrieve One Document
要检索匹配特定条件的单个文档,请将 getSingleResult() 方法链接到 createQuery() 方法。
以下示例从 sample_mflix.movies集合中检索 title 值为 "Best in Show" 的单个文档:
var singleResult = session.createQuery("from Movie where title = :t", Movie.class) .setParameter("t", "Best in Show") .getSingleResult(); System.out.println("Title: " + singleResult.getTitle() + ", Year: " + singleResult.getYear());
Title: Best in Show, Year: 2000
var singleResult = entityManager.createQuery("select m from Movie m where m.title = :t", Movie.class) .setParameter("t", "Best in Show") .getSingleResult(); System.out.println("Title: " + singleResult.getTitle() + ", Year: " + singleResult.getYear());
Title: Best in Show, Year: 2000
运行条件API查询
您可以使用 Jakarta Persistence Criteria API以编程方式构建类型安全的查询,而不是使用 HQL 或 JPQL 语句创建查询筛选器。
要使用 Criteria API创建查询,请执行以下操作:
从会话或实体管理器创建
CriteriaBuilder对象。从构建器创建一个
CriteriaQuery对象,并指定要查询的实体。使用
CriteriaQuery类提供的查询方法来指定查询条件。
提示
要学习;了解有关 Criteria API 的更多信息,请参阅 Jakarta EE 文档中的使用 Criteria API创建查询。
以下示例使用 Criteria API从 sample_mflix.movies集合中检索year 值为 1925 的所有文档:
CriteriaBuilder cb = session.getCriteriaBuilder(); CriteriaQuery<Movie> cq = cb.createQuery(Movie.class); Root<Movie> movieRoot = cq.from(Movie.class); cq.select(movieRoot).where(cb.equal(movieRoot.get("year"), 1925)); session.createQuery(cq).getResultList() .forEach(m -> System.out.println(m.getTitle()));
Lady Windermere's Fan Clash of the Wolves Grass: A Nation's Battle for Life
CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<Movie> cq = cb.createQuery(Movie.class); Root<Movie> movieRoot = cq.from(Movie.class); cq.select(movieRoot).where(cb.equal(movieRoot.get("year"), 1925)); entityManager.createQuery(cq).getResultList() .forEach(m -> System.out.println(m.getTitle()));
Lady Windermere's Fan Clash of the Wolves Grass: A Nation's Battle for Life
运行原生查询
要运行Hibernate ORM 扩展当前不支持的MongoDB查询,您可以将查询作为MongoDB查询语言 (MQL)声明给 createNativeQuery() 方法。
自定义查询筛选器
本节介绍如何使用操作符创建以下类型的查询筛选器:
使用比较筛选器
您可以在查询语句中使用以下操作符将字段值与指定的查询值进行比较:
=:等值匹配<>:不等式匹配>:大于比较>=:大于或等于比较<:小于比较<=:小于或等于比较
以下示例从 sample_mflix.movies集合中检索 year 值大于或等于 2015 的文档:
var comparisonResult = session.createQuery("from Movie where year >= :y", Movie.class) .setParameter("y", 2015) .getResultList(); for (var m : comparisonResult) { System.out.println("Title: " + m.getTitle()); }
Title: Jurassic World Title: The Stanford Prison Experiment Title: Ex Machina Title: Ant-Man Title: The Danish Girl Title: The Wedding Ringer Title: Good Ol' Boy Title: A Tale of Love and Darkness Title: Aloha ...
var comparisonResult = entityManager.createQuery("select m from Movie m where m.year >= :y", Movie.class) .setParameter("y", 2015) .getResultList(); for (var m : comparisonResult) { System.out.println("Title: " + m.getTitle()); }
Title: Jurassic World Title: The Stanford Prison Experiment Title: Ex Machina Title: Ant-Man Title: The Danish Girl Title: The Wedding Ringer Title: Good Ol' Boy Title: A Tale of Love and Darkness Title: Aloha ...
使用逻辑筛选器
您可以在查询语句中使用以下操作符来组合多个查询条件:
and:匹配所有条件or:匹配任何条件not: Invert criteria
以下示例从 sample_mflix.movies集合中检索 title 值为 "The Godfather" 且 year 值为 1972 的文档:
var logicalResult = session.createQuery("from Movie where title = :t and year = :y", Movie.class) .setParameter("t", "The Godfather") .setParameter("y", 1972) .getSingleResult(); System.out.println("Title: " + logicalResult.getTitle());
Title: The Godfather
var logicalResult = entityManager.createQuery("select m from Movie m where m.title = :t and m.year = :y", Movie.class) .setParameter("t", "The Godfather") .setParameter("y", 1972) .getSingleResult(); System.out.println("Title: " + logicalResult.getTitle());
Title: The Godfather
修改查询结果
本节介绍如何通过以下方式修改查询结果:
对结果进行排序
您可以在查询声明中使用 order by 子句,按指定字段的值对查询结果进行排序。默认下,order by 子句按升序对文档进行排序。要按降序对文档进行排序,请将 desc 关键字附加到字段名称。
以下示例从 sample_mflix.movies集合中检索匹配的文档,并按 year字段降序对它们进行排序:
var sortResult = session.createQuery("from Movie where title = :t order by year desc", Movie.class) .setParameter("t", "Cinderella") .getResultList(); for (var m : sortResult) { System.out.println("Title: " + m.getTitle() + ", Year: " + m.getYear()); }
Title: Cinderella, Year: 2015 Title: Cinderella, Year: 1997 Title: Cinderella, Year: 1950
var sortResult = entityManager.createQuery("select m from Movie m where m.title = :t order by m.year desc", Movie.class) .setParameter("t", "Cinderella") .getResultList(); for (var m : sortResult) { System.out.println("Title: " + m.getTitle() + ", Year: " + m.getYear()); }
Title: Cinderella, Year: 2015 Title: Cinderella, Year: 1997 Title: Cinderella, Year: 1950
跳过结果
您可以通过将 setFirstResult() 方法链接到查询来跳过查询结果中指定数量的文档。将要跳过的初始文档数作为参数传递给此方法。
以下示例运行与前面的示例相同的查询,但跳过结果中的第一个匹配文档:
var skipResult = session.createQuery("from Movie where title = :t order by year desc", Movie.class) .setParameter("t", "Cinderella") .setFirstResult(1) .getResultList(); for (var m : skipResult) { System.out.println("Title: " + m.getTitle() + ", Year: " + m.getYear()); }
Title: Cinderella, Year: 1997 Title: Cinderella, Year: 1950
var skipResult = entityManager.createQuery("select m from Movie m where m.title = :t order by m.year desc", Movie.class) .setParameter("t", "Cinderella") .setFirstResult(1) .getResultList(); for (var m : skipResult) { System.out.println("Title: " + m.getTitle() + ", Year: " + m.getYear()); }
Title: Cinderella, Year: 1997 Title: Cinderella, Year: 1950
限制结果
您可以通过将 setMaxResults() 方法链接到查询来限制查询返回的文档数量。将要返回的最大文档数作为参数传递给此方法。
以下示例运行与排序示例相同的查询,但在结果中最多返回两个匹配的文档:
var limitResult = session.createQuery("from Movie where title = :t order by year desc", Movie.class) .setParameter("t", "Cinderella") .setMaxResults(2) .getResultList(); for (var m : limitResult) { System.out.println("Title: " + m.getTitle() + ", Year: " + m.getYear()); }
Title: Cinderella, Year: 2015 Title: Cinderella, Year: 1997
var limitResult = entityManager.createQuery("select m from Movie m where m.title = :t order by m.year desc", Movie.class) .setParameter("t", "Cinderella") .setMaxResults(2) .getResultList(); for (var m : limitResult) { System.out.println("Title: " + m.getTitle() + ", Year: " + m.getYear()); }
Title: Cinderella, Year: 2015 Title: Cinderella, Year: 1997
或者,可以在 HQL 或 JPQL声明中使用 limit 子句来限制查询返回的文档数量,如以下代码所示:
var limitClauseResult = session.createQuery("from Movie where title = :t order by year desc limit 2", Movie.class) .setParameter("t", "Cinderella") .getResultList();
var limitClauseResult = entityManager.createQuery("select m from Movie m where m.title = :t order by m.year desc limit 2", Movie.class) .setParameter("t", "Cinderella") .getResultList();
高级字段查询
本节介绍如何对以下字段运行查询:
查询主键字段
要根据 ObjectId 值检索文档,如果使用会话,可以将此值作为参数传递给 get() 方法;如果使用实体管理器,则可以将此值作为参数传递给 find() 方法。
以下示例通过文档的 ObjectId 值从 sample_mflix.movies集合中检索文档:
var movieById = session.get(Movie.class, new ObjectId("573a13a8f29313caabd1d53c"));
var movieById = entityManager.find(Movie.class, new ObjectId("573a13a8f29313caabd1d53c"));
查询嵌入式字段
您可以通过创建 @Struct 聚合可嵌入对象来表示MongoDB嵌入式文档。您无法对单个可嵌入值创建查询筛选器,但可以使用 Hibernate ORM 扩展来获取与特定父实体关联的 @Struct 聚合可嵌入值。
以下示例从 sample_mflix.movies集合中检索 title 值为 "Hairspray" 的文档。然后,代码获取存储 Awards @Struct 聚合可嵌入类型的 awards字段,并打印 Awards 可嵌入类型的 wins字段:
var embeddedResult = session.createQuery("select awards from Movie where title = :title", Awards.class) .setParameter("title", "Hairspray") .getResultList(); for (var a : embeddedResult) { System.out.println("Award wins: " + a.getWins()); }
Award wins: 0 Award wins: 21
var embeddedResult = entityManager.createQuery("select m.awards from Movie m where m.title = :title", Awards.class) .setParameter("title", "Hairspray") .getResultList(); for (var a : embeddedResult) { System.out.println("Award wins: " + a.getWins()); }
Award wins: 0 Award wins: 21
查询数组字段
Hibernate ORM 扩展支持以下用于查询大量字段的函数:
array_contains():匹配大量字段包含指定值的文档array_contains_nullable():匹配大量字段包含指定值(包括null值)的文档array_includes():匹配大量字段包含另一个大量值的文档array_includes_nullable():匹配大量字段包含另一个大量值(包括null值)的文档
以下示例使用 array_contains() 函数检索sample_mflix.movies集合中 cast大量字段的值为 "Kathryn Hahn" 的文档:
var arrayResult = session.createQuery("from Movie where array_contains(cast, :actor)", Movie.class) .setParameter("actor", "Kathryn Hahn") .getResultList(); for (var m : arrayResult) { System.out.println("Title: " + m.getTitle()); }
Title: How to Lose a Guy in 10 Days Title: The Secret Life of Walter Mitty Title: Bad Words Title: Afternoon Delight Title: The D Train
var arrayResult = entityManager.createQuery("select m from Movie m where array_contains(m.cast, :actor)", Movie.class) .setParameter("actor", "Kathryn Hahn") .getResultList(); for (var m : arrayResult) { System.out.println("Title: " + m.getTitle()); }
Title: How to Lose a Guy in 10 Days Title: The Secret Life of Walter Mitty Title: Bad Words Title: Afternoon Delight Title: The D Train
更多信息
要学习;了解有关对MongoDB数据执行其他操作的更多信息,请参阅执行增删改查操作指南。
要学习;了解有关使用 HQL 和 JPQL运行查询的更多信息,请参阅 Hibernate ORM 文档中的 Hibernate 查询语言指南。