Skip to content

Commit 226fd9a

Browse files
committed
document dynamic restrictions in Jakarta Data repos
1 parent bd557ef commit 226fd9a

File tree

2 files changed

+69
-4
lines changed

2 files changed

+69
-4
lines changed

documentation/src/main/asciidoc/repositories/Pagination.adoc

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ An <<find-method,automatic>> or <<query-method,annotated>> query method may have
88

99
Before we see this, let's see how we can refer to a field of an entity in a completely typesafe way.
1010

11+
[[data-static-metamodel]]
1112
=== The static metamodel
1213

1314
You might already be familiar with the Jakarta Persistence static metamodel.
@@ -59,6 +60,7 @@ This example looks superficially more typesafe.
5960
But since Hibernate Data Repositories already validates the content of the `@OrderBy` annotation at compile time, it's not really better.
6061
====
6162

63+
[[dynamic-sorting]]
6264
=== Dynamic sorting
6365

6466
Dynamic sorting criteria are expressed using the types `Sort` and `Order`:
@@ -100,7 +102,7 @@ The method might now be called like this:
100102
var books =
101103
library.books(pattern, year,
102104
Order.of(_Book.title.ascIgnoreCase(),
103-
_Book.isbn.asc());
105+
_Book.isbn.asc()));
104106
----
105107

106108
Dynamic sorting criteria may be combined with static criteria.
@@ -115,6 +117,7 @@ List<Book> books(@Pattern String title, Year yearPublished,
115117

116118
We're not convinced this is very useful in practice.
117119

120+
[[limits]]
118121
=== Limits
119122

120123
A `Limit` is the simplest way to express a subrange of query results.
@@ -141,6 +144,7 @@ var books =
141144

142145
A more sophisticated approach is provided by `PageRequest`.
143146

147+
[[offset-based-pagination]]
144148
=== Offset-based pagination
145149

146150
A `PageRequest` is superficially similar to a `Limit`, except that it's specified in terms of:
@@ -218,6 +222,7 @@ We'll refer to this as _offset-based pagination_.
218222
A problem with this approach is that it's quite vulnerable to missed or duplicate results when the database is modified between page requests.
219223
Therefore, Jakarta Data offers an alternative solution, which we'll call _key-based pagination_.
220224

225+
[[key-based-pagination]]
221226
=== Key-based pagination
222227

223228
In key-based pagination, the query results must be totally ordered by a unique key of the result set.
@@ -241,7 +246,7 @@ The difference is that we must declare our repository method to return `Cursored
241246
@OrderBy("title")
242247
@OrderBy("isbn")
243248
CursoredPage<Book> books(@Pattern String title, Year yearPublished,
244-
PageRequest pageRequest);
249+
PageRequest pageRequest);
245250
----
246251

247252
On the other hand, with key-based pagination, Hibernate must do some work under the covers rewriting our query.
@@ -258,6 +263,63 @@ Direct API support for key-based pagination originated in the work of Hibernate
258263
It was adopted from there by the Jakarta Data specification, and is now even available in Hibernate ORM via the link:{doc-javadoc-url}org/hibernate/query/KeyedPage.html[`KeyedPage`]/link:{doc-javadoc-url}org/hibernate/query/KeyedResultList.html[`KeyedResultList`] API.
259264
****
260265

266+
[[dynamic-restrictions]]
267+
=== Dynamic restrictions
268+
269+
Jakarta Data 1.0 does not include an API for programmatically specifying restrictions, but for now we may use the native link:{doc-javadoc-url}org/hibernate/query/restriction/Restriction.html[`Restriction`] API in Hibernate 7.
270+
271+
[NOTE]
272+
====
273+
Restrictions will be standardized by Jakarta Data 1.1.
274+
====
275+
276+
Hibernate, an atomic `Restriction` is formed from:
277+
278+
- a reference to a JPA `SingularAttribute`, usually obtained via the _Jakarta Persistence_ (not Jakarta Data) static metamodel, together with
279+
- a `Range` of allowed values for that attribute.
280+
281+
A query method may have a parameter of type `Restriction`, for example:
282+
283+
[source,java]
284+
----
285+
@Find
286+
List<Book> books(Restriction<Book> restriction,
287+
Order<Book> order);
288+
----
289+
290+
This method would be called like this:
291+
292+
[source,java]
293+
----
294+
var books =
295+
library.books(Restriction.contains(Book_.title, "Hibernate"),
296+
Order.of(_Book.title.ascIgnoreCase(),
297+
_Book.isbn.asc()));
298+
----
299+
300+
Notice the mix of metamodels here: `Book_` is the Persistence metamodel, and `_Book` is the Data metamodel.
301+
302+
It's even possible to directly use a link:{doc-javadoc-url}org/hibernate/query/range/Range.html[`Range`] to restrict a given property or field of an entity:
303+
304+
[source,java]
305+
----
306+
@Find
307+
List<Book> books(Range<String> title, Range<Year> yearPublished,
308+
Order<Book> order);
309+
----
310+
311+
There are various kinds of `Range`, including lists, patterns, and intervals:
312+
313+
[source,java]
314+
----
315+
var books =
316+
library.books(Range.prefix("Hibernate"),
317+
Range.closed(Year.of(2000), Year.of(2009)),
318+
Order.of(_Book.title.ascIgnoreCase(),
319+
_Book.isbn.asc()));
320+
----
321+
322+
[[advanced-query-control]]
261323
=== Advanced control over querying
262324

263325
For more advanced usage, an automatic or annotated query method may be declared to return `jakarta.persistence.Query`, `jakarta.persistence.TypedQuery`, link:{doc-javadoc-url}org/hibernate/query/Query.html[`org.hibernate.query.Query`], or link:{doc-javadoc-url}org/hibernate/query/SelectionQuery.html[`org.hibernate.query.SelectionQuery`].
@@ -269,11 +331,11 @@ SelectionQuery<Book> booksQuery(@Pattern String title, Year yearPublished);
269331
270332
default List<Book> booksQuery(String title, Year yearPublished) {
271333
return books(title, yearPublished)
272-
.enableFetchProfile(_Book.PROFILE_WITH_AUTHORS);
334+
.enableFetchProfile(_Book.PROFILE_WITH_AUTHORS)
273335
.setReadOnly(true)
274336
.setTimeout(QUERY_TIMEOUT)
275337
.getResultList();
276338
}
277339
----
278340

279-
This allows for direct control over query execution, without loss of typesafety.
341+
This allows for direct control over query execution, without loss of type safety.

documentation/src/main/asciidoc/repositories/Repositories.adoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ interface Library {
131131

132132
This is our first example of a repository.
133133

134+
[[repository-interfaces]]
134135
=== Repository interfaces
135136

136137
A _repository interface_ is an interface written by you, the application programmer, and annotated `@Repository`.
@@ -165,6 +166,7 @@ We'll discuss each of these kinds of method soon.
165166
But first we need to ask a more basic question: how are persistence operations organized into repositories, and how do repository interfaces relate to entity types?
166167
The--perhaps surprising--answer is: it's completely up to you.
167168

169+
[[organizing-repository-operations]]
168170
=== Organizing persistence operations
169171

170172
Jakarta Data lets you freely assign persistence operations to repositories according to your own preference.
@@ -536,6 +538,7 @@ List<String> booksByTitle(String pattern);
536538

537539
Unfortunately, native SQL queries cannot be validated at compile time, so if there's anything wrong with our SQL, we won't find out until we run our program.
538540

541+
[[by-and-param]]
539542
=== `@By` and `@Param`
540543

541544
Query methods match method parameters to entity fields or query parameters by name.

0 commit comments

Comments
 (0)