Skip to content

Commit c2c44d3

Browse files
committed
HHH-19364 - Introduce QuerySpecification
1 parent 258c881 commit c2c44d3

23 files changed

+1168
-127
lines changed

documentation/src/main/asciidoc/userguide/Hibernate_User_Guide.adoc

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ include::chapters/caching/Caching.adoc[]
3131
include::chapters/events/Events.adoc[]
3232
include::chapters/query/hql/Query.adoc[]
3333
include::chapters/query/hql/QueryLanguage.adoc[]
34+
include::chapters/query/programmatic/QuerySpecification.adoc[]
3435
include::chapters/query/criteria/Criteria.adoc[]
3536
include::chapters/query/criteria/CriteriaExtensions.adoc[]
3637
include::chapters/query/native/Native.adoc[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
[[QuerySpecification]]
2+
== Programmatic Query Specification
3+
:root-project-dir: ../../../../../../../..
4+
5+
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.
6+
7+
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`.
8+
9+
[NOTE]
10+
====
11+
These APIs are new in 7.0 and considered incubating.
12+
====
13+
14+
[[SelectionSpecification]]
15+
=== SelectionSpecification
16+
17+
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 -
18+
19+
.SelectionSpecification from HQL
20+
====
21+
[source, java, indent=0]
22+
----
23+
SelectionSpecification<Book> spec = session.createSelectionSpecification(
24+
"from Book",
25+
Book.class
26+
);
27+
----
28+
====
29+
30+
or a root entity as the base -
31+
32+
.SelectionSpecification from root entity
33+
====
34+
[source, java, indent=0]
35+
----
36+
SelectionSpecification<Book> spec = session.createSelectionSpecification(Book.class);
37+
----
38+
====
39+
40+
Once we have the `SelectionSpecification` we can adjust the query adding restrictions and sorting -
41+
42+
.Adjusting the SelectionSpecification
43+
====
44+
[source, java, indent=0]
45+
----
46+
// from here we can augment the base query "from Book",
47+
// with either restrictions
48+
spec.restriction(
49+
Restriction.restrict(
50+
Book_.suggestedCost,
51+
Range.closed(10.00, 19.99)
52+
)
53+
);
54+
55+
// or here with some sorting
56+
spec.order(
57+
Order.asc(Book_.suggestedCost)
58+
)
59+
----
60+
====
61+
62+
[NOTE]
63+
====
64+
Notice that generally the JPA static metamodel is a convenient and type-safe way to help build these sorting and restriction references.
65+
====
66+
67+
After adjusting the query, we can obtain the executable `SelectionQuery`:
68+
69+
.Using the SelectionSpecification
70+
====
71+
[source, java, indent=0]
72+
----
73+
SelectionQuery<Book> qry = ds.createQuery();
74+
List<Book> books = qry.getResultList();
75+
----
76+
====
77+
78+
These calls can even be chained, e.g.
79+
80+
.Example of chained calls
81+
====
82+
[source, java, indent=0]
83+
----
84+
SelectionQuery<Book> qry = session.createSelectionSpecification(
85+
"from Book",
86+
Book.class
87+
).restriction(
88+
Restriction.restrict(
89+
Book_.suggestedCost,
90+
Range.closed(10.00, 19.99)
91+
)
92+
).order(
93+
Order.asc(Book_.suggestedCost)
94+
).createQuery();
95+
----
96+
====
97+
98+
[NOTE]
99+
====
100+
We expect, in future releases, to add the ability to handle pagination.
101+
102+
We also expect to add the ability to use <<criteria,Criteria>> references as the base. Possibly even `TypedQueryReference` references.
103+
====
104+
105+
[[MutationSpecification]]
106+
=== MutationSpecification
107+
108+
There is also support for mutation queries through `MutationSpecification`.
109+
At the moment, only update and delete queries are supported. E.g.
110+
111+
.MutationQuery example
112+
====
113+
[source, java, indent=0]
114+
----
115+
MutationQuery<Book> qry = session.createMutationSpecification(
116+
"delete Book",
117+
Book.class
118+
).restriction(
119+
Restriction.restrict(
120+
Book_.suggestedCost,
121+
Range.closed(10.00, 19.99)
122+
)
123+
).createQuery();
124+
----
125+
====

hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java

+40-24
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,23 @@
44
*/
55
package org.hibernate.engine.spi;
66

7-
import java.util.Collection;
8-
import java.util.List;
9-
import java.util.Map;
10-
import java.util.Set;
11-
import java.util.TimeZone;
12-
import java.util.UUID;
13-
7+
import jakarta.persistence.CacheRetrieveMode;
8+
import jakarta.persistence.CacheStoreMode;
9+
import jakarta.persistence.EntityGraph;
10+
import jakarta.persistence.EntityManagerFactory;
11+
import jakarta.persistence.FindOption;
12+
import jakarta.persistence.FlushModeType;
13+
import jakarta.persistence.LockModeType;
14+
import jakarta.persistence.LockOption;
15+
import jakarta.persistence.RefreshOption;
16+
import jakarta.persistence.TypedQueryReference;
17+
import jakarta.persistence.criteria.CriteriaDelete;
18+
import jakarta.persistence.criteria.CriteriaQuery;
19+
import jakarta.persistence.criteria.CriteriaSelect;
20+
import jakarta.persistence.criteria.CriteriaUpdate;
1421
import jakarta.persistence.metamodel.EntityType;
22+
import jakarta.persistence.metamodel.Metamodel;
23+
import org.checkerframework.checker.nullness.qual.Nullable;
1524
import org.hibernate.CacheMode;
1625
import org.hibernate.Filter;
1726
import org.hibernate.FlushMode;
@@ -49,31 +58,23 @@
4958
import org.hibernate.query.SelectionQuery;
5059
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
5160
import org.hibernate.query.criteria.JpaCriteriaInsert;
61+
import org.hibernate.query.programmatic.MutationSpecification;
62+
import org.hibernate.query.programmatic.SelectionSpecification;
5263
import org.hibernate.query.spi.QueryImplementor;
5364
import org.hibernate.query.spi.QueryProducerImplementor;
5465
import org.hibernate.query.sql.spi.NativeQueryImplementor;
5566
import org.hibernate.resource.jdbc.spi.JdbcSessionContext;
5667
import org.hibernate.resource.transaction.spi.TransactionCoordinator;
5768
import org.hibernate.stat.SessionStatistics;
58-
59-
import jakarta.persistence.CacheRetrieveMode;
60-
import jakarta.persistence.CacheStoreMode;
61-
import jakarta.persistence.EntityGraph;
62-
import jakarta.persistence.EntityManagerFactory;
63-
import jakarta.persistence.FindOption;
64-
import jakarta.persistence.FlushModeType;
65-
import jakarta.persistence.LockModeType;
66-
import jakarta.persistence.LockOption;
67-
import jakarta.persistence.RefreshOption;
68-
import jakarta.persistence.TypedQueryReference;
69-
import jakarta.persistence.criteria.CriteriaDelete;
70-
import jakarta.persistence.criteria.CriteriaQuery;
71-
import jakarta.persistence.criteria.CriteriaSelect;
72-
import jakarta.persistence.criteria.CriteriaUpdate;
73-
import jakarta.persistence.metamodel.Metamodel;
74-
import org.checkerframework.checker.nullness.qual.Nullable;
7569
import org.hibernate.type.format.FormatMapper;
7670

71+
import java.util.Collection;
72+
import java.util.List;
73+
import java.util.Map;
74+
import java.util.Set;
75+
import java.util.TimeZone;
76+
import java.util.UUID;
77+
7778
/**
7879
* A wrapper class that delegates all method invocations to a delegate instance of
7980
* {@link SessionImplementor}. This is useful for custom implementations of that
@@ -656,6 +657,21 @@ public MutationQuery createNamedMutationQuery(String name) {
656657
return delegate.createNamedMutationQuery( name );
657658
}
658659

660+
@Override
661+
public <T> SelectionSpecification<T> createSelectionSpecification(String hql, Class<T> resultType) {
662+
return delegate.createSelectionSpecification( hql, resultType );
663+
}
664+
665+
@Override
666+
public <T> SelectionSpecification<T> createSelectionSpecification(Class<T> rootEntityType) {
667+
return delegate.createSelectionSpecification( rootEntityType );
668+
}
669+
670+
@Override
671+
public <T> MutationSpecification<T> createMutationSpecification(String hql, Class<T> mutationTarget) {
672+
return delegate.createMutationSpecification( hql, mutationTarget );
673+
}
674+
659675
@Override
660676
public MutationQuery createNativeMutationQuery(String sqlString) {
661677
return delegate.createNativeMutationQuery( sqlString );

hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java

+38-22
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,26 @@
44
*/
55
package org.hibernate.engine.spi;
66

7-
import java.util.Collection;
8-
import java.util.List;
9-
import java.util.Map;
10-
import java.util.function.Supplier;
11-
127
import jakarta.persistence.CacheRetrieveMode;
138
import jakarta.persistence.CacheStoreMode;
9+
import jakarta.persistence.ConnectionConsumer;
10+
import jakarta.persistence.ConnectionFunction;
11+
import jakarta.persistence.EntityGraph;
12+
import jakarta.persistence.EntityManagerFactory;
13+
import jakarta.persistence.FindOption;
14+
import jakarta.persistence.FlushModeType;
15+
import jakarta.persistence.LockModeType;
16+
import jakarta.persistence.LockOption;
17+
import jakarta.persistence.RefreshOption;
18+
import jakarta.persistence.TypedQuery;
19+
import jakarta.persistence.TypedQueryReference;
20+
import jakarta.persistence.criteria.CriteriaDelete;
21+
import jakarta.persistence.criteria.CriteriaQuery;
22+
import jakarta.persistence.criteria.CriteriaSelect;
23+
import jakarta.persistence.criteria.CriteriaUpdate;
1424
import jakarta.persistence.metamodel.EntityType;
25+
import jakarta.persistence.metamodel.Metamodel;
26+
import org.checkerframework.checker.nullness.qual.Nullable;
1527
import org.hibernate.CacheMode;
1628
import org.hibernate.Filter;
1729
import org.hibernate.FlushMode;
@@ -41,25 +53,14 @@
4153
import org.hibernate.query.SelectionQuery;
4254
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
4355
import org.hibernate.query.criteria.JpaCriteriaInsert;
56+
import org.hibernate.query.programmatic.MutationSpecification;
57+
import org.hibernate.query.programmatic.SelectionSpecification;
4458
import org.hibernate.stat.SessionStatistics;
4559

46-
import jakarta.persistence.ConnectionConsumer;
47-
import jakarta.persistence.ConnectionFunction;
48-
import jakarta.persistence.EntityGraph;
49-
import jakarta.persistence.EntityManagerFactory;
50-
import jakarta.persistence.FindOption;
51-
import jakarta.persistence.FlushModeType;
52-
import jakarta.persistence.LockModeType;
53-
import jakarta.persistence.LockOption;
54-
import jakarta.persistence.RefreshOption;
55-
import jakarta.persistence.TypedQuery;
56-
import jakarta.persistence.TypedQueryReference;
57-
import jakarta.persistence.criteria.CriteriaDelete;
58-
import jakarta.persistence.criteria.CriteriaQuery;
59-
import jakarta.persistence.criteria.CriteriaSelect;
60-
import jakarta.persistence.criteria.CriteriaUpdate;
61-
import jakarta.persistence.metamodel.Metamodel;
62-
import org.checkerframework.checker.nullness.qual.Nullable;
60+
import java.util.Collection;
61+
import java.util.List;
62+
import java.util.Map;
63+
import java.util.function.Supplier;
6364

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

753+
@Override
754+
public <T> SelectionSpecification<T> createSelectionSpecification(String hql, Class<T> resultType) {
755+
return this.lazySession.get().createSelectionSpecification( hql, resultType );
756+
}
757+
758+
@Override
759+
public <T> SelectionSpecification<T> createSelectionSpecification(Class<T> rootEntityType) {
760+
return this.lazySession.get().createSelectionSpecification( rootEntityType );
761+
}
762+
763+
@Override
764+
public <T> MutationSpecification<T> createMutationSpecification(String hql, Class<T> mutationTarget) {
765+
return this.lazySession.get().createMutationSpecification( hql, mutationTarget );
766+
}
767+
752768
@SuppressWarnings("rawtypes")
753769
@Override
754770
@Deprecated

hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import jakarta.persistence.criteria.CriteriaQuery;
1111
import jakarta.persistence.criteria.CriteriaUpdate;
1212
import org.checkerframework.checker.nullness.qual.Nullable;
13-
1413
import org.hibernate.CacheMode;
1514
import org.hibernate.Filter;
1615
import org.hibernate.FlushMode;
@@ -37,6 +36,8 @@
3736
import org.hibernate.query.SelectionQuery;
3837
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
3938
import org.hibernate.query.criteria.JpaCriteriaInsert;
39+
import org.hibernate.query.programmatic.MutationSpecification;
40+
import org.hibernate.query.programmatic.SelectionSpecification;
4041
import org.hibernate.query.spi.QueryImplementor;
4142
import org.hibernate.query.spi.QueryProducerImplementor;
4243
import org.hibernate.query.sql.spi.NativeQueryImplementor;
@@ -228,6 +229,21 @@ public MutationQuery createNamedMutationQuery(String name) {
228229
return delegate.createNamedMutationQuery( name );
229230
}
230231

232+
@Override
233+
public <T> SelectionSpecification<T> createSelectionSpecification(String hql, Class<T> resultType) {
234+
return delegate.createSelectionSpecification( hql, resultType );
235+
}
236+
237+
@Override
238+
public <T> SelectionSpecification<T> createSelectionSpecification(Class<T> rootEntityType) {
239+
return delegate.createSelectionSpecification( rootEntityType );
240+
}
241+
242+
@Override
243+
public <T> MutationSpecification<T> createMutationSpecification(String hql, Class<T> mutationTarget) {
244+
return delegate.createMutationSpecification( hql, mutationTarget );
245+
}
246+
231247
@Override
232248
public MutationQuery createNativeMutationQuery(String sqlString) {
233249
return delegate.createNativeMutationQuery( sqlString );

0 commit comments

Comments
 (0)