minor improvements to new HQL guide
This commit is contained in:
parent
f3fddf02da
commit
fab058a3a1
|
@ -957,7 +957,7 @@ where total > 100.0
|
|||
----
|
||||
|
||||
[[between-predicate]]
|
||||
==== `between` predicate
|
||||
==== The `between` predicate
|
||||
|
||||
The ternary `between` operator, and its negation, `not between`, determine if a value falls within a range.
|
||||
|
||||
|
@ -988,6 +988,32 @@ The following operators make it easier to deal with null values.
|
|||
from Author where nomDePlume is not null
|
||||
----
|
||||
|
||||
[[collection-operators]]
|
||||
==== Collection predicates
|
||||
|
||||
The following operators apply to collection-valued attributes and to-many associations.
|
||||
|
||||
[cols="15,15,20,~"]
|
||||
|===
|
||||
| Operator | Negation | Type | Semantics
|
||||
|
||||
| `is empty` | `is not empty` | Unary postfix | `true` if the collection or association on the left has no elements
|
||||
| `member of` | `not member of` | Binary | `true` if the value on the left is a member of the collection or association on the right
|
||||
|===
|
||||
|
||||
[[empty-collection-predicate-example]]
|
||||
[source, hql]
|
||||
----
|
||||
from Author where books is empty
|
||||
----
|
||||
|
||||
[[member-of-collection-predicate-example]]
|
||||
[source, hql]
|
||||
----
|
||||
from Author as author, Book as book
|
||||
where author member of book.authors
|
||||
----
|
||||
|
||||
[[like-predicate]]
|
||||
==== String pattern matching
|
||||
|
||||
|
@ -1019,7 +1045,7 @@ The optional `escape` character allows a pattern to include a literal `_` or `%`
|
|||
As you can guess, `not like` and `not ilike` are the enemies of `like` and `ilike`, and evaluate to the exact opposite boolean values.
|
||||
|
||||
[[in-predicate]]
|
||||
==== `in` predicate
|
||||
==== The `in` predicate
|
||||
|
||||
The `in` predicates evaluates to true if the value to its left is in ... well, whatever it finds to its right.
|
||||
|
||||
|
@ -1063,6 +1089,11 @@ where type(payment) in (CreditCardPayment, WireTransferPayment)
|
|||
from Author as author
|
||||
where author.person.name in (select name from OldAuthorData)
|
||||
----
|
||||
[source, hql]
|
||||
----
|
||||
from Book as book
|
||||
where :edition in elements(book.editions)
|
||||
----
|
||||
|
||||
This example doesn't work on every database:
|
||||
|
||||
|
@ -1118,7 +1149,7 @@ from Publisher pub where :title = some(select title from pub.books)
|
|||
----
|
||||
|
||||
[[exists-predicate]]
|
||||
==== `exists` predicate
|
||||
==== The `exists` predicate
|
||||
|
||||
The unary prefix `exists` operator evaluates to true if the thing to its right is nonempty.
|
||||
|
||||
|
@ -1143,34 +1174,6 @@ where exists (
|
|||
)
|
||||
----
|
||||
|
||||
|
||||
[[collection-operators]]
|
||||
==== Collection predicates
|
||||
|
||||
The following operators apply to collection-valued attributes and to-many associations.
|
||||
|
||||
[cols="15,15,20,~"]
|
||||
|===
|
||||
| Operator | Negation | Type | Semantics
|
||||
|
||||
| `is empty` | `is not empty` | Unary postfix | `true` if the collection or association on the left has no elements
|
||||
| `member of` | `not member of` | Binary | `true` if the value on the left is a member of the collection or association on the right
|
||||
|===
|
||||
|
||||
[[empty-collection-predicate-example]]
|
||||
[source, hql]
|
||||
----
|
||||
from Author where books is empty
|
||||
----
|
||||
|
||||
[[member-of-collection-predicate-example]]
|
||||
[source, hql]
|
||||
----
|
||||
from Author as author, Book as book
|
||||
where author member of book.authors
|
||||
----
|
||||
|
||||
|
||||
[[logical-operators]]
|
||||
==== Logical operators
|
||||
|
||||
|
|
|
@ -452,7 +452,7 @@ The example above is equivalent to:
|
|||
----
|
||||
select book
|
||||
from Book as book
|
||||
join book.publisher pub
|
||||
join book.publisher as pub
|
||||
where pub.name like :pubName
|
||||
----
|
||||
|
||||
|
|
|
@ -101,6 +101,7 @@ So let's restrict our result set to data from our own more civilized times:
|
|||
|
||||
[[group-by-having-example]]
|
||||
[source, hql]
|
||||
[%unbreakable]
|
||||
----
|
||||
select book.isbn,
|
||||
year(order.dateTime) as year,
|
||||
|
@ -120,13 +121,21 @@ The `having` restriction is applied after grouping and aggregation has already b
|
|||
=== Projection
|
||||
|
||||
The `select` list identifies which objects and values to return as the query results.
|
||||
This operation is called _projection_.
|
||||
|
||||
NOTE: This operation is called _projection_.
|
||||
[source,antlrv4]
|
||||
----
|
||||
selectClause
|
||||
: "SELECT" "DISTINCT"? selection (","" selection)*
|
||||
----
|
||||
|
||||
Any of the expression types discussed in <<expressions>> may occur in the projection list, unless otherwise noted.
|
||||
|
||||
TIP: If a query has no explicit `select` list, the projection is inferred from the entities and joins occurring in the `from` clause, together with the result type specified by the call to `createQuery()`.
|
||||
It's better to specify the projection explicitly, except in the simplest cases.
|
||||
[TIP]
|
||||
====
|
||||
If a query has no explicit `select` list, then, as we saw <<select-simplest-example,much earlier>>, the projection is inferred from the entities and joins occurring in the `from` clause, together with the result type specified by the call to `createQuery()`.
|
||||
But it's better to specify the projection explicitly, except in the simplest cases.
|
||||
====
|
||||
|
||||
There might be multiple items in a projection list, in which case each query result is a tuple, and this poses a problem:
|
||||
Java doesn't have a good way to represent tuples.
|
||||
|
@ -134,13 +143,19 @@ Java doesn't have a good way to represent tuples.
|
|||
If there's just one projected item in the `select` list, then, no sweat, that's the type of each query result.
|
||||
There's no need to bother with trying to represent a "tuple of length 1".
|
||||
|
||||
But if there are multiple expressions in the select list then:
|
||||
[source, java]
|
||||
[%unbreakable]
|
||||
----
|
||||
List<String> results =
|
||||
entityManager.createQuery("select title from Book", String.class)
|
||||
.getResultList();
|
||||
----
|
||||
|
||||
- by default, each query result is packaged as an array of type `Object[]`, or
|
||||
- if explicitly requested by passing the class `Tuple` to `createQuery()`, the query result is packaged as an instance of `javax.persistence.Tuple`.
|
||||
But if there are multiple expressions in the select list then, by default, each query result is packaged as an array of type `Object[]`.
|
||||
|
||||
[[select-clause-projection-example]]
|
||||
[source, java]
|
||||
[%unbreakable]
|
||||
----
|
||||
List<Object[]> results =
|
||||
entityManager.createQuery("select title, left(book.text, 200) from Book",
|
||||
|
@ -151,7 +166,11 @@ for (var result : results) {
|
|||
String preamble = (String) result[1];
|
||||
}
|
||||
----
|
||||
|
||||
Or, if explicitly requested by passing the class `Tuple` to `createQuery()`, the query result is packaged as an instance of `javax.persistence.Tuple`.
|
||||
|
||||
[source, java]
|
||||
[%unbreakable]
|
||||
----
|
||||
List<Tuple> results =
|
||||
entityManager.createQuery("select title as title, left(book.text, 200) as preamble from Book",
|
||||
|
@ -164,10 +183,9 @@ for (Tuple tuple : tuples) {
|
|||
----
|
||||
|
||||
The names of the `Tuple` elements are determined by the aliases given to the projected items in the select list.
|
||||
If no aliases are specified, the elements may be accessed by their position in the list (positions are numbered from 0).
|
||||
If no aliases are specified, the elements may be accessed by their position in the list, where the first item is assigned the position zero.
|
||||
|
||||
Unfortunately, neither `Object[]` nor `Tuple` lets us access an individual item in a result tuple of an HQL query without explicitly specifying the type of the item.
|
||||
(Using a typecast in the case of `Object[]`, or by passing the class object to `get()` in the case of `Tuple`.)
|
||||
Unfortunately, neither `Object[]` nor `Tuple` lets us access an individual item in a result tuple of an HQL query without explicitly specifying the type of the item, either using a typecast in the case of `Object[]`, or by passing the class object to `get()` in the case of `Tuple`.
|
||||
But there's another option, as we're about to see.
|
||||
|
||||
Simplifying slightly, the BNF for a projected item is:
|
||||
|
@ -175,13 +193,20 @@ Simplifying slightly, the BNF for a projected item is:
|
|||
[[select-item-bnf]]
|
||||
[source, antlrv4]
|
||||
----
|
||||
include::{extrasdir}/select_item_bnf.txt[]
|
||||
selection
|
||||
: (expression | instantiation) alias?
|
||||
|
||||
instantiation
|
||||
: "NEW" instantiationTarget "(" selection ("," selection)* ")"
|
||||
|
||||
alias
|
||||
: "AS"? identifier
|
||||
----
|
||||
|
||||
where `instantiatiationArgs` is essentially a nested projection list.
|
||||
Where the list of ``selection``s in an `instantiation` is essentially a nested projection list.
|
||||
|
||||
So there's a special expression type that's only legal in the select clause: the `instantiation` rule in the BNF above.
|
||||
Let's see what it does.
|
||||
// Let's see what it does.
|
||||
|
||||
[[select-new]]
|
||||
==== Instantiation
|
||||
|
@ -190,6 +215,7 @@ The `select new` construct packages the query results into a user-written Java c
|
|||
|
||||
[[select-clause-dynamic-instantiation-example]]
|
||||
[source, java]
|
||||
[%unbreakable]
|
||||
----
|
||||
record BookSummary(String title, String summary) {}
|
||||
|
||||
|
@ -216,6 +242,7 @@ Alternatively, using the syntax `select new map`, the query may specify that eac
|
|||
|
||||
[[select-clause-dynamic-map-instantiation-example]]
|
||||
[source, java]
|
||||
[%unbreakable]
|
||||
----
|
||||
List<Map> results =
|
||||
entityManager.createQuery("select new map(title as title, left(book.text, 200) as summary) from Book",
|
||||
|
@ -224,12 +251,13 @@ List<Map> results =
|
|||
----
|
||||
|
||||
The keys of the map are determined by the aliases given to the projected items in the select list.
|
||||
If no aliases are specified, the key of an item is its position in the list (positions are numbered from 0).
|
||||
If no aliases are specified, the key of an item is its position in the list, where the first item is assigned the position zero.
|
||||
|
||||
Or, using the syntax `select new list`, the query may specify that each result should be packaged as a list:
|
||||
|
||||
[[select-clause-dynamic-list-instantiation-example]]
|
||||
[source, java]
|
||||
[%unbreakable]
|
||||
----
|
||||
List<List> results =
|
||||
entityManager.createQuery("select new list(title as title, left(book.text, 200) as summary) from Book",
|
||||
|
@ -242,7 +270,7 @@ List<List> results =
|
|||
This is an older syntax, that predates JPQL.
|
||||
In hindsight, it's hard to see what advantage `List<Object>` offers compared to `Object[]`.
|
||||
We mention it here only for completeness.
|
||||
On the other hand, `Map` is a perfectly fine alternative `Tuple`, but of course isn't portable to other implementations of JPA.
|
||||
On the other hand, `Map` is a perfectly fine alternative `Tuple`, but of course it isn't portable to other implementations of JPA.
|
||||
====
|
||||
|
||||
[[distinct]]
|
||||
|
@ -295,18 +323,21 @@ The standard aggregate functions defined in both ANSI SQL and JPQL are these one
|
|||
|
||||
[[aggregate-functions-example]]
|
||||
[source, hql]
|
||||
[%unbreakable]
|
||||
----
|
||||
select count(distinct item.book)
|
||||
from Item as item
|
||||
where year(item.order.dateTime) = :year
|
||||
----
|
||||
[source, hql]
|
||||
[%unbreakable]
|
||||
----
|
||||
select sum(item.quantity) as totalSales
|
||||
from Item as item
|
||||
where item.book.isbn = :isbn
|
||||
----
|
||||
[source, hql]
|
||||
[%unbreakable]
|
||||
----
|
||||
select
|
||||
year(item.order.dateTime) as year,
|
||||
|
@ -316,6 +347,7 @@ where item.book.isbn = :isbn
|
|||
group by year(item.order.dateTime)
|
||||
----
|
||||
[source, hql]
|
||||
[%unbreakable]
|
||||
----
|
||||
select
|
||||
month(item.order.dateTime) as month,
|
||||
|
@ -385,6 +417,7 @@ All aggregate functions support the inclusion of a _filter clause_, a sort of mi
|
|||
|
||||
[[aggregate-functions-filter-example]]
|
||||
[source, hql]
|
||||
[%unbreakable]
|
||||
----
|
||||
select
|
||||
year(item.order.dateTime) as year,
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
(expression | instantiation) alias?
|
||||
selection
|
||||
: (expression | instantiation) alias?
|
||||
|
||||
instantiation
|
||||
: "NEW" instantiationTarget "(" instantiationArguments ")"
|
||||
: "NEW" instantiationTarget "(" selection ("," selection)* ")"
|
||||
|
||||
alias
|
||||
: "AS"? IDENTIFIER
|
Loading…
Reference in New Issue