Skip to content

Commit a18e5ad

Browse files
committed
HHH-18979 document Restriction in Short Guide
1 parent 00b1be2 commit a18e5ad

File tree

2 files changed

+110
-20
lines changed

2 files changed

+110
-20
lines changed

documentation/src/main/asciidoc/introduction/Interacting.adoc

Lines changed: 67 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,8 @@ query.where(builder.like(root.get(Book_.title), builder.literal("Hibernate%")));
924924
query.orderBy(builder.asc(root.get(Book_.title)), builder.desc(root.get(Book_.isbn)));
925925
List<Book> matchingBooks = session.createSelectionQuery(query).getResultList();
926926
----
927+
This is starting to get a bit messy.
928+
In Hibernate 7, we can often use <<restrictions-and-ordering,`Restriction`>> instead.
927929
====
928930

929931
Do you find some of the code above a bit too verbose?
@@ -1034,8 +1036,59 @@ List<Book> books =
10341036
You can call stored procedures using `createStoredProcedureQuery()` or `createStoredProcedureCall()`.
10351037
====
10361038

1039+
[[restrictions-and-ordering]]
1040+
=== Restrictions and ordering
1041+
1042+
We've already seen how the JPA <<criteria-queries,Criteria Query API>> can be used to construct a query completely programmatically.
1043+
The Criteria API is powerful, but for the most common scenarios it's at least arguably overkill.
1044+
The <<criteria-definition,`CriteriaDefinition`>> class helps a bit, but it doesn't completely eliminate the verbosity of programmatic query definition.
1045+
1046+
In Hibernate 7, there's a new option, a very ergonomic API for programmatically adding restrictions or ordering to an existing query before executing it.
1047+
(Actually, the ordering part of this was introduced in Hibernate 6.5.)
1048+
This new API:
1049+
1050+
- isn't part of the Criteria Query API, and so we don't need a `CriteriaQuery` object to make use of it,
1051+
- works with both HQL and Criteria queries, and even with <<named-queries,named HQL queries>>, and
1052+
- is optimized for the case of a query which returns its single root entity.
1053+
1054+
[source,java]
1055+
----
1056+
var query = session.createSelectionQuery("from Book where year(publicationDate) > 2000", Book.class);
1057+
if (titlePattern != null) {
1058+
query.addRestriction(Restriction.like(Book_.title, namePattern));
1059+
}
1060+
if (isbns != null && !isbns.isEmpty()) {
1061+
query.addRestriction(Restriction.in(Book_.isbn, isbns))
1062+
}
1063+
query.setOrder(List.of(Order.asc(Book_.title), Order.asc(Book_.isbn)));
1064+
List<Book> matchingBooks = query.getResultList();
1065+
1066+
----
1067+
1068+
Notice that:
1069+
1070+
- The link:{doc-javadoc-url}org/hibernate/query/Restriction.html[`Restriction`] interface has static methods for constructing a variety of different kinds of restriction in a completely typesafe way.
1071+
- Similarly, the link:{doc-javadoc-url}org/hibernate/query/Order.html[`Order`] class has a variety of static methods for constructing different kinds of ordering criteria.
1072+
1073+
We need the following methods of `SelectionQuery`:
1074+
1075+
.Methods for query restriction and ordering
1076+
[%breakable,cols="30,~,^15"]
1077+
|===
1078+
| Method name | Purpose | JPA-standard
1079+
1080+
| `addRestriction()` | Add a restriction on the query results | &#10006;
1081+
| `setOrder()` | Specify how the query results should be ordered | &#10006;
1082+
|===
1083+
1084+
Unfortunately, `Restriction` and `Order` can't be used with JPA's `TypedQuery` interface, and JPA has no built-in alternative, so if we're using `EntityManager`, we need to call `unwrap()` to obtain a `SelectionQuery`.
1085+
1086+
Alternatively, `Restriction` and `Order` can be used with <<paging-and-ordering,generated query or finder methods>>, and even with link:{doc-data-repositories-url}[Jakarta Data repositories].
1087+
1088+
Programmatic restrictions, and especially programmatic ordering, are often used together with pagination.
1089+
10371090
[[pagination]]
1038-
=== Limits, pagination, and ordering
1091+
=== Limits and pagination
10391092

10401093
If a query might return more results than we can handle at one time, we may specify:
10411094

@@ -1099,10 +1152,21 @@ long pages = results / MAX_RESULTS + (results % MAX_RESULTS == 0 ? 0 : 1);
10991152
List<Book> books = query.setMaxResults(MAX_RESULTS).getResultList();
11001153
----
11011154

1102-
A closely-related issue is ordering.
1155+
.Methods for query limits, pagination, and ordering
1156+
[%breakable,cols="30,~,^15"]
1157+
|===
1158+
| Method name | Purpose | JPA-standard
1159+
1160+
| `setMaxResults()` | Set a limit on the number of results returned by a query | &#10004;
1161+
| `setFirstResult()` | Set an offset on the results returned by a query | &#10004;
1162+
| `setPage()` | Set the limit and offset by specifying a `Page` object | &#10006;
1163+
| `getResultCount()` | Determine how many results the query would return in the absence of any limit or offset | &#10006;
1164+
|===
1165+
11031166
It's quite common for pagination to be combined with the need to order query results by a field that's determined at runtime.
1104-
So, as an alternative to the HQL `order by` clause, `SelectionQuery` offers the ability to specify that the query results should be ordered by one or more fields of the entity type returned by the query:
1167+
The `Order` class we just met <<restrictions-and-ordering,above>> provides the ability to specify that the query results should be ordered by one or more fields of the entity type returned by the query:
11051168

1169+
[[query-order-example]]
11061170
[source,java]
11071171
----
11081172
List<Book> books =
@@ -1113,20 +1177,6 @@ List<Book> books =
11131177
.getResultList();
11141178
----
11151179

1116-
Unfortunately, there's no way to do this using JPA's `TypedQuery` interface.
1117-
1118-
.Methods for query limits, pagination, and ordering
1119-
[%breakable,cols="30,~,^15"]
1120-
|===
1121-
| Method name | Purpose | JPA-standard
1122-
1123-
| `setMaxResults()` | Set a limit on the number of results returned by a query | &#10004;
1124-
| `setFirstResult()` | Set an offset on the results returned by a query | &#10004;
1125-
| `setPage()` | Set the limit and offset by specifying a `Page` object | &#10006;
1126-
| `setOrder()` | Specify how the query results should be ordered | &#10006;
1127-
| `getResultCount()` | Determine how many results the query would return in the absence of any limit or offset | &#10006;
1128-
|===
1129-
11301180
The approach to pagination we've just seen is sometimes called _offset-based pagination_.
11311181
Since Hibernate 6.5, there's an alternative approach, which offers some advantages, though it's a little more difficult to use.
11321182

documentation/src/main/asciidoc/introduction/Processor.adoc

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,24 @@ The `@Pattern` annotation may be applied to a parameter of type `String`, indica
555555
List<Book> getBooksByTitle(@Pattern String title, Type type);
556556
----
557557

558+
Even better, a parameter may be of type `Range<T>`, where `T` is the type of the matching field.
559+
560+
[source,java]
561+
----
562+
@Find
563+
List<Book> getBooksByTitle(Range<String> title, Type type);
564+
----
565+
566+
The link:{doc-javadoc-url}org/hibernate/query/range/Range.html[`Range`] interface has a variety of `static` methods the caller may use to construct different kinds of ranges.
567+
For example, `Range.pattern()` constructs a `Range` representing a pattern.
568+
569+
[source,java]
570+
----
571+
List<Book> books =
572+
// returns books with titles beginning with "hibernate"
573+
queries.getBooksByTitle(Range.prefix("hibernate", false), type);
574+
----
575+
558576
A finder method may specify <<fetch-profiles,fetch profiles>>, for example:
559577

560578
[source,java]
@@ -588,7 +606,7 @@ This lets us declare which associations of `Book` should be pre-fetched by annot
588606
// ----
589607

590608
[[paging-and-ordering]]
591-
=== Paging and ordering
609+
=== Paging, ordering, and restrictions
592610

593611
Optionally, a query method--or a finder method which returns multiple results--may have additional "magic" parameters which do not map to query parameters:
594612

@@ -603,6 +621,7 @@ Optionally, a query method--or a finder method which returns multiple results--m
603621
| `Order<Object[]>` | Specifies a column to order by, if the query returns a projection list | Order.asc(1)
604622
| `List<Object[]>` +
605623
(or varargs) | Specifies columns to order by, if the query returns a projection list | List.of(Order.asc(1), Order.desc(2))
624+
| `Restriction<? super E>` | Specifies a restriction used to filter query results | Restriction.startsWith("Hibernate")
606625
|===
607626

608627
Thus, if we redefine our earlier query method as follows:
@@ -636,8 +655,28 @@ interface Queries {
636655
}
637656
----
638657

639-
This gives some dynamic control over query execution, but what if we would like direct control over the `Query` object?
640-
Well, let's talk about the return type.
658+
Similarly, we may define a query method which accepts an arbitrary ``Restriction``:
659+
660+
[source,java]
661+
----
662+
interface Queries {
663+
@Find
664+
List<Book> findBooks(Restriction<? super Book> restriction, Order<? super Book>... order);
665+
}
666+
----
667+
668+
As we <<restrictions-and-ordering,saw earlier>>, the `Restriction` interface has a variety of `static` methods for constructing restrictions.
669+
670+
[source,java]
671+
----
672+
List<Book> books =
673+
// returns books with titles beginning with "hibernate", sorted by title
674+
queries.findBooks(Restriction.startsWith(Book_.title, "hibernate", false),
675+
Order.asc(Book_.title));
676+
----
677+
678+
This gives some dynamic control over query execution.
679+
We'll see <<return-query-from-method-example,below>> that it's even possible for the caller to gain direct control over the `Query` object.
641680

642681
[[key-based-paging]]
643682
=== Key-based pagination
@@ -722,6 +761,7 @@ List<IsbnTitle> listIsbnAndTitleForEachBook(Page page);
722761

723762
A query method might even return `TypedQuery` or `SelectionQuery`:
724763

764+
[[return-query-from-method-example]]
725765
[source,java]
726766
----
727767
@HQL("where title like :title")

0 commit comments

Comments
 (0)