Skip to content

HHH-19364 - Introduce QuerySpecification #10034

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ include::chapters/caching/Caching.adoc[]
include::chapters/events/Events.adoc[]
include::chapters/query/hql/Query.adoc[]
include::chapters/query/hql/QueryLanguage.adoc[]
include::chapters/query/programmatic/QuerySpecification.adoc[]
include::chapters/query/criteria/Criteria.adoc[]
include::chapters/query/criteria/CriteriaExtensions.adoc[]
include::chapters/query/native/Native.adoc[]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
[[QuerySpecification]]
== Programmatic Query Specification
:root-project-dir: ../../../../../../../..

Hibernate offers an API for creating a representation of a query, adjusting that representation programmatically, and then creating an executable form of the query. The idea is similar in concept to <<chapters/query/criteria/Criteria.adoc#criteria,criteria queries>>, but focused on ease-of-use and less verbosity.

There is support for both <<SelectionSpecification,selection>> and <<MutationSpecification,mutation>> queries via the `SelectionSpecification` and `MutationSpecification` contracts, respectively. These can be obtained from both `Session` and `StatelessSession`.

[NOTE]
====
These APIs are new in 7.0 and considered incubating.
====

[[SelectionSpecification]]
=== SelectionSpecification

A `SelectionSpecification` allows to iteratively build a query from a "base", adjust the query by adding sorting and restrictions and finally creating an executable <<hql-SelectionQuery,`SelectionQuery`>>. We can use HQL as the base -

.SelectionSpecification from HQL
====
[source, java, indent=0]
----
SelectionSpecification<Book> spec = session.createSelectionSpecification(
"from Book",
Book.class
);
----
====

or a root entity as the base -

.SelectionSpecification from root entity
====
[source, java, indent=0]
----
SelectionSpecification<Book> spec = session.createSelectionSpecification(Book.class);
----
====

Once we have the `SelectionSpecification` we can adjust the query adding restrictions and sorting -

.Adjusting the SelectionSpecification
====
[source, java, indent=0]
----
// from here we can augment the base query "from Book",
// with either restrictions
spec.restriction(
Restriction.restrict(
Book_.suggestedCost,
Range.closed(10.00, 19.99)
)
);

// or here with some sorting
spec.order(
Order.asc(Book_.suggestedCost)
)
----
====

[NOTE]
====
Notice that generally the JPA static metamodel is a convenient and type-safe way to help build these sorting and restriction references.
====

After adjusting the query, we can obtain the executable `SelectionQuery`:

.Using the SelectionSpecification
====
[source, java, indent=0]
----
SelectionQuery<Book> qry = ds.createQuery();
List<Book> books = qry.getResultList();
----
====

These calls can even be chained, e.g.

.Example of chained calls
====
[source, java, indent=0]
----
SelectionQuery<Book> qry = session.createSelectionSpecification(
"from Book",
Book.class
).restriction(
Restriction.restrict(
Book_.suggestedCost,
Range.closed(10.00, 19.99)
)
).order(
Order.asc(Book_.suggestedCost)
).createQuery();
----
====

[NOTE]
====
We expect, in future releases, to add the ability to handle pagination.

We also expect to add the ability to use <<criteria,Criteria>> references as the base. Possibly even `TypedQueryReference` references.
====

[[MutationSpecification]]
=== MutationSpecification

There is also support for mutation queries through `MutationSpecification`.
At the moment, only update and delete queries are supported. E.g.

.MutationQuery example
====
[source, java, indent=0]
----
MutationQuery<Book> qry = session.createMutationSpecification(
"delete Book",
Book.class
).restriction(
Restriction.restrict(
Book_.suggestedCost,
Range.closed(10.00, 19.99)
)
).createQuery();
----
====
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,23 @@
*/
package org.hibernate.engine.spi;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;

import jakarta.persistence.CacheRetrieveMode;
import jakarta.persistence.CacheStoreMode;
import jakarta.persistence.EntityGraph;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.FindOption;
import jakarta.persistence.FlushModeType;
import jakarta.persistence.LockModeType;
import jakarta.persistence.LockOption;
import jakarta.persistence.RefreshOption;
import jakarta.persistence.TypedQueryReference;
import jakarta.persistence.criteria.CriteriaDelete;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.CriteriaSelect;
import jakarta.persistence.criteria.CriteriaUpdate;
import jakarta.persistence.metamodel.EntityType;
import jakarta.persistence.metamodel.Metamodel;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.CacheMode;
import org.hibernate.Filter;
import org.hibernate.FlushMode;
Expand Down Expand Up @@ -49,31 +58,23 @@
import org.hibernate.query.SelectionQuery;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaCriteriaInsert;
import org.hibernate.query.programmatic.MutationSpecification;
import org.hibernate.query.programmatic.SelectionSpecification;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.query.spi.QueryProducerImplementor;
import org.hibernate.query.sql.spi.NativeQueryImplementor;
import org.hibernate.resource.jdbc.spi.JdbcSessionContext;
import org.hibernate.resource.transaction.spi.TransactionCoordinator;
import org.hibernate.stat.SessionStatistics;

import jakarta.persistence.CacheRetrieveMode;
import jakarta.persistence.CacheStoreMode;
import jakarta.persistence.EntityGraph;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.FindOption;
import jakarta.persistence.FlushModeType;
import jakarta.persistence.LockModeType;
import jakarta.persistence.LockOption;
import jakarta.persistence.RefreshOption;
import jakarta.persistence.TypedQueryReference;
import jakarta.persistence.criteria.CriteriaDelete;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.CriteriaSelect;
import jakarta.persistence.criteria.CriteriaUpdate;
import jakarta.persistence.metamodel.Metamodel;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.type.format.FormatMapper;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;

/**
* A wrapper class that delegates all method invocations to a delegate instance of
* {@link SessionImplementor}. This is useful for custom implementations of that
Expand Down Expand Up @@ -656,6 +657,21 @@ public MutationQuery createNamedMutationQuery(String name) {
return delegate.createNamedMutationQuery( name );
}

@Override
public <T> SelectionSpecification<T> createSelectionSpecification(String hql, Class<T> resultType) {
return delegate.createSelectionSpecification( hql, resultType );
}

@Override
public <T> SelectionSpecification<T> createSelectionSpecification(Class<T> rootEntityType) {
return delegate.createSelectionSpecification( rootEntityType );
}

@Override
public <T> MutationSpecification<T> createMutationSpecification(String hql, Class<T> mutationTarget) {
return delegate.createMutationSpecification( hql, mutationTarget );
}

@Override
public MutationQuery createNativeMutationQuery(String sqlString) {
return delegate.createNativeMutationQuery( sqlString );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,26 @@
*/
package org.hibernate.engine.spi;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

import jakarta.persistence.CacheRetrieveMode;
import jakarta.persistence.CacheStoreMode;
import jakarta.persistence.ConnectionConsumer;
import jakarta.persistence.ConnectionFunction;
import jakarta.persistence.EntityGraph;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.FindOption;
import jakarta.persistence.FlushModeType;
import jakarta.persistence.LockModeType;
import jakarta.persistence.LockOption;
import jakarta.persistence.RefreshOption;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.TypedQueryReference;
import jakarta.persistence.criteria.CriteriaDelete;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.CriteriaSelect;
import jakarta.persistence.criteria.CriteriaUpdate;
import jakarta.persistence.metamodel.EntityType;
import jakarta.persistence.metamodel.Metamodel;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.CacheMode;
import org.hibernate.Filter;
import org.hibernate.FlushMode;
Expand Down Expand Up @@ -41,25 +53,14 @@
import org.hibernate.query.SelectionQuery;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaCriteriaInsert;
import org.hibernate.query.programmatic.MutationSpecification;
import org.hibernate.query.programmatic.SelectionSpecification;
import org.hibernate.stat.SessionStatistics;

import jakarta.persistence.ConnectionConsumer;
import jakarta.persistence.ConnectionFunction;
import jakarta.persistence.EntityGraph;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.FindOption;
import jakarta.persistence.FlushModeType;
import jakarta.persistence.LockModeType;
import jakarta.persistence.LockOption;
import jakarta.persistence.RefreshOption;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.TypedQueryReference;
import jakarta.persistence.criteria.CriteriaDelete;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.CriteriaSelect;
import jakarta.persistence.criteria.CriteriaUpdate;
import jakarta.persistence.metamodel.Metamodel;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

/**
* This helper class allows decorating a Session instance, while the
Expand Down Expand Up @@ -749,6 +750,21 @@ public MutationQuery createNamedMutationQuery(String name) {
return this.lazySession.get().createNamedMutationQuery( name );
}

@Override
public <T> SelectionSpecification<T> createSelectionSpecification(String hql, Class<T> resultType) {
return this.lazySession.get().createSelectionSpecification( hql, resultType );
}

@Override
public <T> SelectionSpecification<T> createSelectionSpecification(Class<T> rootEntityType) {
return this.lazySession.get().createSelectionSpecification( rootEntityType );
}

@Override
public <T> MutationSpecification<T> createMutationSpecification(String hql, Class<T> mutationTarget) {
return this.lazySession.get().createMutationSpecification( hql, mutationTarget );
}

@SuppressWarnings("rawtypes")
@Override
@Deprecated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.CriteriaUpdate;
import org.checkerframework.checker.nullness.qual.Nullable;

import org.hibernate.CacheMode;
import org.hibernate.Filter;
import org.hibernate.FlushMode;
Expand All @@ -37,6 +36,8 @@
import org.hibernate.query.SelectionQuery;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaCriteriaInsert;
import org.hibernate.query.programmatic.MutationSpecification;
import org.hibernate.query.programmatic.SelectionSpecification;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.query.spi.QueryProducerImplementor;
import org.hibernate.query.sql.spi.NativeQueryImplementor;
Expand Down Expand Up @@ -228,6 +229,21 @@ public MutationQuery createNamedMutationQuery(String name) {
return delegate.createNamedMutationQuery( name );
}

@Override
public <T> SelectionSpecification<T> createSelectionSpecification(String hql, Class<T> resultType) {
return delegate.createSelectionSpecification( hql, resultType );
}

@Override
public <T> SelectionSpecification<T> createSelectionSpecification(Class<T> rootEntityType) {
return delegate.createSelectionSpecification( rootEntityType );
}

@Override
public <T> MutationSpecification<T> createMutationSpecification(String hql, Class<T> mutationTarget) {
return delegate.createMutationSpecification( hql, mutationTarget );
}

@Override
public MutationQuery createNativeMutationQuery(String sqlString) {
return delegate.createNativeMutationQuery( sqlString );
Expand Down
Loading