Overview
In this guide, you can learn how to use the MongoDB Extension for Hibernate ORM to specify a database query.
You can refine the set of documents that a query returns by creating a query filter. A query filter is an expression that specifies the search criteria MongoDB uses to match documents in a read or write operation. To create MongoDB query filters, use Hibernate Query Language (HQL) or Jakarta Persistence Query Language (JPQL) statements.
Tip
To learn more about HQL and JPQL syntax, see A Guide to Hibernate Query Language in the Hibernate ORM documentation.
Note
Query Support
The MongoDB Extension for Hibernate ORM does not support all MongoDB and Hibernate query features. To learn more, see Query Support on the Feature Compatibility page.
Sample Data
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.time.Instant; import java.util.List; import jakarta.persistence.Embedded; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.Id; import jakarta.persistence.OneToMany; 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; private Instant released; private Awards awards; private List<Comment> comments; public Movie(String title, String plot, int year, List<String> cast, List<String> directors, Instant released, Awards awards) { this.title = title; this.plot = plot; this.year = year; this.cast = cast; this.directors = directors; this.released = released; this.awards = awards; } 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; } public Awards getAwards() { return awards; } public void setAwards(Awards awards) { this.awards = awards; } public Instant getReleased() { return released; } public void setReleased(Instant released) { this.released = released; } public List<Comment> getComments() { return comments; } public void setComments(List<Comment> comments) { this.comments = comments; } }
To learn how to create a Java application that uses the MongoDB Extension for Hibernate ORM to interact with this MongoDB sample collection, see the Get Started tutorial.
The code examples on this page also use the following Awards @Struct aggregate embeddable:
@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() { } public int getWins() { return wins; } public void setWins(int wins) { this.wins = wins; } public int getNominations() { return nominations; } public void setNominations(int nominations) { this.nominations = nominations; } public String getText() { return text; } public void setText(String text) { this.text = text; } }
Important
Persistence Contexts
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. Use HQL to define session queries, and use JPQL to define entity manager queries.
Before running the examples in this guide, ensure that you add persistence context and transaction management code to your application that resembles the following code:
var sf = HibernateUtil.getSessionFactory(); Session session = sf.openSession(); Transaction tx = session.beginTransaction(); // ... Perform CRUD operations here tx.commit(); session.close(); sf.close();
To use a session, you must create a HibernateUtil.java file that configures a SessionFactory. To learn more, see the Configure your Application step of the Get Started tutorial.
// 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;
To use an EntityManager, you must create a persistence.xml file that declares a persistence unit. To learn more, see the Tutorial using JPA-standard APIs in the Hibernate ORM documentation.
Required Null Semantics Configuration Property
Before you can create a SessionFactory instance, you must set the com.mongodb.hibernate.semantics.nulls configuration property to MQL. This property is required, has no default value, and currently accepts no other value. If you omit the property, or set it to another value, the Hibernate ORM extension throws a HibernateException.
When you set com.mongodb.hibernate.semantics.nulls to MQL, the property declares that null-related behavior follows the MongoDB Query Language (MQL) that the Hibernate ORM extension produces during translation, not the three-valued logic defined by SQL null semantics.
Because the Hibernate ORM extension does not guarantee a specific translation, the results of null-related behavior can change between releases.
The following example sets the com.mongodb.hibernate.semantics.nulls property in your hibernate.properties file:
com.mongodb.hibernate.semantics.nulls=MQL
Use Comparison Operators
You can use the following operators in your query statements to compare field values to specified query values:
=: Equality matching<>: Inequality matching>: Greater than comparisons>=: Greater than or equal comparisons<: Less than comparisons<=: Less than or equal comparisons
The following example retrieves documents that have a year value greater than or equal to 2015 from the sample_mflix.movies collection:
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()); }
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()); }
Compare Multiple Fields at Once
A row-value predicate compares a parenthesized list of fields to a parenthesized list of values in a single expression. The Hibernate ORM extension supports row-value predicates that use the following operators:
=: Equality matching<>: Inequality matchingINandNOT IN: Matching against a list of value rows
You can supply the values as bound parameters or as literals, and each list must contain the same number of components.
The following example retrieves documents that have a title value of "Jurassic World" and a year value of 2015 from the sample_mflix.movies collection:
var rowValueResult = session.createQuery("from Movie where (title, year) = (:t, :y)", Movie.class) .setParameter("t", "Jurassic World") .setParameter("y", 2015) .getResultList(); for (var m : rowValueResult) { System.out.println("Title: " + m.getTitle()); }
var rowValueResult = entityManager.createQuery("select m from Movie m where (m.title, m.year) = (:t, :y)", Movie.class) .setParameter("t", "Jurassic World") .setParameter("y", 2015) .getResultList(); for (var m : rowValueResult) { System.out.println("Title: " + m.getTitle()); }
The Hibernate ORM extension compares each pair of components separately. The preceding row-value predicate translates to the following $match stage:
{ "$match": { "$and": [ { "title": { "$eq": "Jurassic World" } }, { "year": { "$eq": 2015 } } ] } }
A row-value predicate that uses the <> operator translates to the same $and expression wrapped in a $nor expression.
If you compare the fields to another list of fields, as in where (title1, year1) = (title2, year2), the Hibernate ORM extension translates the predicate to a $expr expression instead.
You can use a row-value predicate in a SELECT clause. The predicate evaluates to a boolean value in the projection.
Note
Null Semantics
Row-value predicates follow MongoDB Query Language null semantics rather than Hibernate ORM ternary logic, so a <> predicate matches documents in which a component is null or missing. To learn more, see the Compare Field Values to Null section of this guide.
Match a List of Value Rows
To match a row of fields against multiple rows of values, use the IN operator. The following example retrieves documents that match either of two title and year pairs:
var rowValueInResult = session.createQuery( "from Movie where (title, year) in (('Jurassic World', 2015), ('Ex Machina', 2015))", Movie.class) .getResultList(); for (var m : rowValueInResult) { System.out.println("Title: " + m.getTitle()); }
var rowValueInResult = entityManager.createQuery( "select m from Movie m where (m.title, m.year) in (('Jurassic World', 2015), ('Ex Machina', 2015))", Movie.class) .getResultList(); for (var m : rowValueInResult) { System.out.println("Title: " + m.getTitle()); }
The preceding row-value predicate translates to the following $match stage:
{ "$match": { "$or": [ { "$and": [ { "title": { "$eq": "Jurassic World" } }, { "year": { "$eq": 2015 } } ] }, { "$and": [ { "title": { "$eq": "Ex Machina" } }, { "year": { "$eq": 2015 } } ] } ] } }
If the list contains only one row of values, the predicate translates to the $and expression alone. A NOT IN predicate translates to the $or expression wrapped in a $nor expression.
Important
Ordering Comparisons
The Hibernate ORM extension does not support row-value predicates that use the >, >=, <, or <= operator. These predicates cause the Hibernate ORM extension to throw a FeatureNotSupportedException.
Compare Field Values to Null
When you compare a field to null by using a comparison operator, the Hibernate ORM extension applies MongoDB Query Language null semantics instead of the ternary logic that Hibernate ORM defines.
Note
Hibernate Ternary Logic for null
Hibernate ORM v6.3 and later evaluate a comparison to null as null, which a predicate treats as false. The Hibernate ORM extension does not implement this behavior. To learn more about which features the Hibernate ORM extension supports, see Data Type Support on the Feature Compatibility page.
MongoDB comparison operators evaluate both a missing field and a field that stores an explicit null value to null. As a result, a = null comparison matches documents in which the field stores null and documents in which the field is absent. A <> null or != null comparison matches documents in which the field stores any other value.
The following example uses the = operator to retrieve documents that have a null or missing cast value from the sample_mflix.movies collection:
var nullComparisonResult = session.createQuery("from Movie where cast = null", Movie.class) .getResultList(); for (var m : nullComparisonResult) { System.out.println("Title: " + m.getTitle()); }
var nullComparisonResult = entityManager.createQuery("select m from Movie m where m.cast = null", Movie.class) .getResultList(); for (var m : nullComparisonResult) { System.out.println("Title: " + m.getTitle()); }
The Hibernate ORM extension translates this query to the following $match stage:
{ "$match": { "cast": { "$eq": null } } }
Comparisons that use the >, >=, <, and <= operators follow the MongoDB BSON comparison order for non-existent fields. Because null is its own BSON type, A >= null or <= null comparison matches documents in which the field stores null or is missing. A > null or < null comparison matches no documents.
To match null and missing values by using a predicate instead of a comparison operator, see the IS NULL and IS NOT NULL sections of this guide.
Use Computed Expressions
You can compute a value from field values, literals, and query parameters. You can then return that value in a SELECT clause or compare it in a WHERE clause. The Hibernate ORM extension translates a computed expression to a MongoDB aggregation expression.
The Hibernate ORM extension supports the following arithmetic operators in computed expressions:
+: Addition, which translates to$add-: Subtraction, which translates to$subtract*: Multiplication, which translates to$multiply/: Division, which translates to$divideUnary
-and+
Note
Division Always Returns an Integer
The Hibernate ORM translates division in a MongoDB aggregation pipeline to the MongoDB $divide operator, which always returns a double. The Hibernate ORM extension truncates the result by wrapping $divide in $toInt, or $toLong if the result type is BIGINT. This behavior applies to the / operator and the Criteria API quot() method, regardless of whether you enable the Hibernate ORM PORTABLE_INTEGER_DIVISION setting.
The div operator is not supported.
You can also use the comparison operators described in the Use Comparison Operators section within computed expressions.
Important
Operand Limitations
An operand of a computed expression must be a field reference, a literal, or a query parameter. The Hibernate ORM extension does not support function calls as operands.
Because Hibernate ORM rewrites the HQL % operator to a mod() function call, % is also unsupported. To perform a modulo operation, use the Criteria API CriteriaBuilder.mod() method, which translates to the MongoDB $mod operator.
The Criteria API CriteriaBuilder.quot() method behaves identically to the / operator.
Perform Arithmetic in a Projection
When you select a computed expression, the Hibernate ORM extension adds the computed value to the $project stage. If you assign the expression an alias by using the AS keyword, the Hibernate ORM extension uses the alias as the projection key. Otherwise, it generates a key in the form #c_<n>.
The following example computes the age of each "Hairspray" movie in the sample_mflix.movies collection by subtracting the year field value from a query parameter:
var arithmeticResult = session.createQuery( "select title, :currentYear - year as age from Movie where title = :title", Object[].class) .setParameter("currentYear", 2026) .setParameter("title", "Hairspray") .getResultList(); for (var row : arithmeticResult) { System.out.println("Title: " + row[0] + ", Age: " + row[1]); }
var arithmeticResult = entityManager.createQuery( "select m.title, :currentYear - m.year as age from Movie m where m.title = :title", Object[].class) .setParameter("currentYear", 2026) .setParameter("title", "Hairspray") .getResultList(); for (var row : arithmeticResult) { System.out.println("Title: " + row[0] + ", Age: " + row[1]); }
The Hibernate ORM extension translates the preceding computed expression to the following $project stage:
{ "$project": { "title": true, "age": { "$subtract": [ 2026, "$year" ] }, "_id": 0 } }
Filter on a Computed Expression
In the WHERE clause, you can compare a computed expression to a value or compare two field values to each other. When either side of a comparison is not a direct field reference or value, the Hibernate ORM extension wraps the comparison in the MongoDB $expr operator. Comparisons between a field and a value continue to use the compact { field: { operator: value } } form.
The following example retrieves "Hairspray" movies released fewer than 20 years before 2026:
var computedFilterResult = session.createQuery( "from Movie where title = :title and :currentYear - year < 20", Movie.class) .setParameter("title", "Hairspray") .setParameter("currentYear", 2026) .getResultList(); for (var m : computedFilterResult) { System.out.println("Title: " + m.getTitle() + ", Year: " + m.getYear()); }
var computedFilterResult = entityManager.createQuery( "select m from Movie m where m.title = :title and :currentYear - m.year < 20", Movie.class) .setParameter("title", "Hairspray") .setParameter("currentYear", 2026) .getResultList(); for (var m : computedFilterResult) { System.out.println("Title: " + m.getTitle() + ", Year: " + m.getYear()); }
The Hibernate ORM extension translates the preceding query to the following $match stage, in which the title comparison uses the compact form and the computed comparison uses $expr:
{ "$match": { "$and": [ { "title": { "$eq": "Hairspray" } }, { "$expr": { "$lt": [ { "$subtract": [ 2026, "$year" ] }, 20 ] } } ] } }
Project a Comparison Result
You can use a comparison in a SELECT statement to return its boolean result instead of using the comparison as a filter.
The following example returns the title of each "Hairspray" movie and whether the movie was released after 2000:
var comparisonResult = session.createQuery( "select title, year > 2000 as isRecent from Movie where title = :title", Object[].class) .setParameter("title", "Hairspray") .getResultList(); for (var row : comparisonResult) { System.out.println("Title: " + row[0] + ", Recent: " + row[1]); }
var comparisonResult = entityManager.createQuery( "select m.title, m.year > 2000 as isRecent from Movie m where m.title = :title", Object[].class) .setParameter("title", "Hairspray") .getResultList(); for (var row : comparisonResult) { System.out.println("Title: " + row[0] + ", Recent: " + row[1]); }
The Hibernate ORM extension translates the preceding comparison to the following $project stage:
{ "$project": { "title": true, "isRecent": { "$gt": [ "$year", 2000 ] }, "_id": 0 } }
Use Predicate Operators
The Hibernate ORM extension supports the following predicate comparison operators in your query statements:
Note
The Hibernate ORM extension does not support all comparison operators. To learn more about support limitations, see Query Support on the Feature Compatibility page.
EXISTS
The EXISTS predicate matches documents that have a specific field and returns only one result for each matching parent document, regardless of how many array elements match.
Note
EXISTS Subquery Limitations
The Hibernate ORM extension supports EXISTS subqueries only over an array field of the parent entity, and only in the WHERE clause. The Hibernate ORM extension does not support EXISTS subqueries that:
Query over an entity without first specifying a field
Compare fields in the same parent document
Appear in the
SELECTclause
For a full example, see the Query on Elements of an Embedded Array section of this guide.
BETWEEN
The BETWEEN predicate matches documents that have a field value within a specified range.
The following example uses the BETWEEN predicate to retrieves documents that have a year value between 2012 and 2013, inclusive, from the sample_mflix.movies collection:
var betweenResult = session.createQuery("from Movie where year between :start and :end", Movie.class) .setParameter("start", 2012) .setParameter("end", 2013) .getResultList(); for (var m : betweenResult) { System.out.println("Title: " + m.getTitle()); }
var betweenResult = entityManager.createQuery("select m from Movie m where m.year between :start and :end", Movie.class) .setParameter("start", 2012) .setParameter("end", 2013) .getResultList(); for (var m : betweenResult) { System.out.println("Title: " + m.getTitle()); }
The Hibernate ORM extension translates the preceding BETWEEN predicate to the following $match stage:
{ "$match": { "year": { "$gte": 2012, "$lte": 2013 } } }
IN
The IN predicate matches documents where a field value equals any value in a specified list. You can supply the list values as literals, named parameters, or positional parameters.
The following example uses the IN predicate to retrieve documents that have a year value of either 1994 or 1996 from the sample_mflix.movies collection:
var inResult = session.createQuery("from Movie where year in (:first, :second)", Movie.class) .setParameter("first", 1994) .setParameter("second", 1996) .getResultList(); for (var m : inResult) { System.out.println("Title: " + m.getTitle()); }
var inResult = entityManager.createQuery("select m from Movie m where m.year in (:first, :second)", Movie.class) .setParameter("first", 1994) .setParameter("second", 1996) .getResultList(); for (var m : inResult) { System.out.println("Title: " + m.getTitle()); }
The Hibernate ORM extension translates the preceding IN predicate to the following $match stage:
{ "$match": { "year": { "$in": [ 1994, 1996 ] } } }
An empty list is valid, but it never matches any documents. For example, year in () matches no documents.
Note
IN Predicate Limitations
The Hibernate ORM extension supports the IN predicate only when the value to the left of IN is a field path. The Hibernate ORM extension does not support IN predicates that:
Take a subquery as the list of values.
Test a value against an array-valued expression, as in
:value in m.cast. To match a value against an array field, use thearray_contains()function instead.
To learn more about querying an array, see the Query an Array Field section of this guide.
NOT IN
The NOT IN predicate matches documents in which a field value does not equal any value in a specified list. NOT IN accepts the same list forms and has the same limitations as the IN predicate.
The following example uses the NOT IN predicate to retrieve documents that have a title value other than "Romeo and Juliet" or "Best in Show" from the sample_mflix.movies collection. Because NOT IN excludes only the listed values, the example calls the setMaxResults() method to limit the result set to ten documents:
var notInResult = session.createQuery("from Movie where title not in (:first, :second)", Movie.class) .setParameter("first", "Romeo and Juliet") .setParameter("second", "Best in Show") .setMaxResults(10) .getResultList(); for (var m : notInResult) { System.out.println("Title: " + m.getTitle()); }
var notInResult = entityManager.createQuery("select m from Movie m where m.title not in (:first, :second)", Movie.class) .setParameter("first", "Romeo and Juliet") .setParameter("second", "Best in Show") .setMaxResults(10) .getResultList(); for (var m : notInResult) { System.out.println("Title: " + m.getTitle()); }
The Hibernate ORM extension translates the preceding NOT IN predicate to the following $match stage:
{ "$match": { "title": { "$nin": [ "Romeo and Juliet", "Best in Show" ] } } }
Because $nin matches every document against an empty list of values, title not in () matches all documents.
Note
NOT IN Limitations
The Hibernate ORM extension supports the NOT IN predicate only when the value to the left of NOT IN is a field path. The Hibernate ORM extension does not support NOT IN predicates that:
Take a subquery as the list of values.
Test a value against an array-valued expression, as in
:value not in m.cast.
IS NULL
The IS NULL predicate matches documents that have a specific field with a null or missing value.
To compare a field to null by using a comparison operator instead of a predicate, see the Compare Field Values to Null section of this guide.
The following example uses the IS NULL predicate to retrieve documents that have a missing or null cast value from the sample_mflix.movies collection:
var isNullResult = session.createQuery("from Movie where cast is null", Movie.class) .getResultList(); for (var m : isNullResult) { System.out.println("Title: " + m.getTitle()); }
var isNullResult = entityManager.createQuery("select m from Movie m where m.cast is null", Movie.class) .getResultList(); for (var m : isNullResult) { System.out.println("Title: " + m.getTitle()); }
The Hibernate ORM extension translates the preceding IS NULL predicate to the following $match stage, which matches documents where the field is explicitly null or missing:
{ "$match": { "cast": { "$eq": null } } }
IS NOT NULL
The IS NOT NULL predicate matches documents that have a field with a non-null or non-missing value.
The following example uses the IS NOT NULL predicate to retrieve documents that have a directors value from the sample_mflix.movies collection:
var isNotNullResult = session.createQuery("from Movie where directors is not null", Movie.class) .getResultList(); for (var m : isNotNullResult) { System.out.println("Title: " + m.getTitle()); }
var isNotNullResultEm = entityManager.createQuery("select m from Movie m where m.directors is not null", Movie.class) .getResultList(); for (var m : isNotNullResultEm) { System.out.println("Title: " + m.getTitle()); }
The Hibernate ORM extension translates the preceding IS NOT NULL predicate to the following $match stage, which matches documents where the field exists and is not explicitly null:
{ "$match": { "directors": { "$ne": null } } }
Use Logical Filters
You can use the following operators in your query statements to combine multiple query criteria:
and: Match all criteriaor: Match any criterianot: Does not match criteria
The following example retrieves a document that has a title value of "The Godfather" and a year value of 1972 from the sample_mflix.movies collection:
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());
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());
Query a Primary Key Field
To retrieve a document based on its ObjectId value, you can pass this value as an argument to the get() method, if you're using a session, or the find() method, if you're using an entity manager.
The following example retrieves a document from the sample_mflix.movies collection by its ObjectId value:
var movieById = session.get(Movie.class, new ObjectId("573a13a8f29313caabd1d53c"));
var movieById = entityManager.find(Movie.class, new ObjectId("573a13a8f29313caabd1d53c"));
Query an Embedded Document
You can represent MongoDB embedded documents by creating @Struct aggregate embeddables. You can then use the Hibernate ORM extension to fetch @Struct aggregate embeddables associated with specific parent entities.
Tip
To learn more about representing embedded documents, see Embedded Data in the Create Entities guide.
The following example retrieves documents that have a title value of "Hairspray" from the sample_mflix.movies collection. Then, the code fetches the awards field, which stores the Awards @Struct aggregate embeddable, and prints the wins field of the Awards embeddable type:
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()); }
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()); }
Query a Field on an Embedded Document
You can reference a field on an embeddable type by using a dotted path expression in the SELECT, WHERE, ORDER BY, and UPDATE clauses of an HQL or JPQL query. You can also chain multiple embeddable names in a path expression to reference a field on a nested embeddable within another embeddable.
Important
Query Limitations
Path expressions do not support embeddable fields that use the @ColumnTransformer annotation to define a custom read expression.
The Hibernate ORM extension also rejects the following @Struct aggregate embeddable mappings when it builds the entity model:
A
@Structaggregate embeddable used as an@IdfieldA polymorphic
@Structaggregate embeddable hierarchy
You can't use a path expression to reference a field on these types.
The following example uses a path expression to filter documents in the sample_mflix.movies collection. There are two "Hairspray" movies in the sample_mflix.movies collection. One "Hairspray" movie is from 1998, and the other is from 2007. The query matches documents that have a title value of "Hairspray" and an awards.wins value greater than 10:
var matchingDocument = session.createQuery("from Movie where title = :title and awards.wins > :minWins", Movie.class) .setParameter("title", "Hairspray") .setParameter("minWins", 10) .getResultList(); for (var m : matchingDocument) { System.out.println("Title: " + m.getTitle() + ", Year: " + m.getYear()); }
var matchingDocument = entityManager.createQuery("select m from Movie m where m.title = :title and m.awards.wins > :minWins", Movie.class) .setParameter("title", "Hairspray") .setParameter("minWins", 10) .getResultList(); for (var m : matchingDocument) { System.out.println("Title: " + m.getTitle() + ", Year: " + m.getYear()); }
Query on Elements of an Embedded Array
When a field of your entity stores an array of @Struct aggregate embeddables, you can use an EXISTS subquery to match parent documents in which at least one element of that array meets your criteria. The Hibernate ORM extension translates the subquery to the MongoDB $elemMatch operator, which applies all criteria to the same array element.
Sample Data
Note
Use the sample_restaurants Database for This Section
The examples in this section use the sample_restaurants.restaurants collection rather than the sample_mflix.movies collection used elsewhere in this guide.
To run these examples, connect to the sample_restaurants database in your connection string. To learn more about the sample_restaurants database, see the Atlas sample datasets.
The following Restaurant entity maps to the restaurants collection and stores a list of Grade embeddables in its grades field:
package org.example; import com.mongodb.hibernate.annotations.ObjectIdGenerator; import jakarta.persistence.Column; import org.bson.types.ObjectId; import java.util.List; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.Table; public class Restaurant { private ObjectId id; private String name; private String borough; private List<Grade> grades; public Restaurant(String name, String borough, List<Grade> grades) { this.name = name; this.borough = borough; this.grades = grades; } public Restaurant() { } public ObjectId getId() { return id; } public void setId(ObjectId id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getBorough() { return borough; } public void setBorough(String borough) { this.borough = borough; } public List<Grade> getGrades() { return grades; } public void setGrades(List<Grade> grades) { this.grades = grades; } }
The following Grade @Struct aggregate embeddable represents each element of the grades array:
package org.example; import jakarta.persistence.*; import org.hibernate.annotations.Struct; public class Grade { private String grade; private int score; public Grade() { } public Grade(String grade, int score) { this.grade = grade; this.score = score; } public String getGrade() { return grade; } public void setGrade(String grade) { this.grade = grade; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } }
Tip
To learn more about arrays of embeddables, see Embedded Data in the Create Entities guide.
Documents in the restaurants collection resemble the following:
{ "_id": { "$oid": "5eb3d668b31de5d588f4292a" }, "name": "Morris Park Bake Shop", "borough": "Bronx", "cuisine": "Bakery", "grades": [ { "date": { "$date": 1393804800000 }, "grade": "A", "score": 2 }, { "date": { "$date": 1299715200000 }, "grade": "B", "score": 14 } ] }
Example
The following example retrieves restaurants that have at least one grades array element with a grade value of "B" and a score value equal to 12. Because both criteria appear in the same subquery, they must match the same array element:
var existsResult = session.createQuery( "from Restaurant r where exists (select g.grade from r.grades g where g.grade = :grade and g.score = :score)", Restaurant.class) .setParameter("grade", "B") .setParameter("score", 12) .getResultList(); for (var r : existsResult) { System.out.println("Name: " + r.getName()); }
var existsResultEm = entityManager.createQuery( "select r from Restaurant r where exists (select g.grade from r.grades g where g.grade = :grade and g.score = :score)", Restaurant.class) .setParameter("grade", "B") .setParameter("score", 12) .getResultList(); for (var r : existsResultEm) { System.out.println("Name: " + r.getName()); }
The Hibernate ORM extension translates the preceding EXISTS subquery to the following $match stage:
{ "$match": { "grades": { "$elemMatch": { "grade": { "$eq": "B" }, "score": { "$eq": 12 } } } } }
Query an Array Field
The Hibernate ORM extension supports the following functions for querying array fields:
array_contains(): Match documents where an array field contains a specified valuearray_contains_nullable(): Match documents where an array field contains a specified value, includingnullvaluesarray_includes(): Match documents where an array field includes another array valuearray_includes_nullable(): Match documents where an array field includes another array value, includingnullvalues
Tip
To learn more about array functions, see Functions for dealing with arrays in the Hibernate ORM user guide.
The following example uses the array_contains() function to retrieve documents that have the value "Kathryn Hahn" in the cast array field from the sample_mflix.movies collection:
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()); }
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()); }
Use Aggregate Functions
You can use aggregate functions in HQL and JPQL queries to group and summarize query results, or to filter grouped results by using a HAVING clause. HQL and JPQL support the following aggregate functions:
count()sum()avg()min()max()
Note
GROUP BY Requirements
Aggregate functions require a GROUP BY clause. A query that uses an aggregate function without grouping results, such as select count(*) from Movie, is not supported yet. Non-aggregate fields in the SELECT clause must also appear in the GROUP BY clause.
The following example groups movies released between 1920 and 1924 by year. For each year, it returns the number of movies and the total runtime for all movies. The HAVING clause includes only the years whose movies have a total runtime greater than 300 minutes, and orders the results by year.
var aggregateResult = session.createQuery( "select year, count(*), sum(runtime) from Movie where year between 1920 and 1924 " + "group by year having sum(runtime) > 300 order by year", Object[].class) .getResultList(); for (var row : aggregateResult) { System.out.println("Year: " + row[0] + ", Count: " + row[1] + ", Total runtime: " + row[2]); }
var aggregateResult = entityManager.createQuery( "select m.year, count(m), sum(m.runtime) from Movie m where m.year between 1920 and 1924 " + "group by m.year having sum(m.runtime) > 300 order by m.year", Object[].class) .getResultList(); for (var row : aggregateResult) { System.out.println("Year: " + row[0] + ", Count: " + row[1] + ", Total runtime: " + row[2]); }
Additional Information
To learn more about performing other operations on your MongoDB data, see the Perform CRUD Operations guide.
To learn how to query across entities that are linked by an association, see the Join Entities Across Collections guide.
To learn how to return part of a datetime value or render a datetime value as a string, see the Use Datetime Functions in Queries guide.
To learn more about using HQL and JPQL to run queries, see A Guide to Hibernate Query Language in the Hibernate ORM documentation.