update documentation to explain implicit collection joins
This commit is contained in:
parent
437da23961
commit
4d024fde8b
|
@ -953,48 +953,20 @@ The syntax is `format(datetime as pattern)`, and the pattern must be written in
|
|||
|
||||
For a full list of `format()` pattern elements, see the Javadoc for https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/dialect/Dialect.html#appendDatetimeFormat[`Dialect#appendDatetimeFormat`].
|
||||
|
||||
[[hql-collection-qualification]]
|
||||
==== Collection elements, map keys, and list indexes
|
||||
[[hql-list-functions]]
|
||||
===== `element()` and `index()`
|
||||
|
||||
The following functions may be applied to a collection-valued path expression to obtain a reference to a list index or map key.
|
||||
A reference to an element or index of <<hql-collection-valued-associations,joined list>>.
|
||||
|
||||
|===
|
||||
| Function | Applies to | Interpretation | Notes
|
||||
[[hql-map-functions]]
|
||||
===== `key()`, `value()`, and `entry()`
|
||||
|
||||
| `value()` or `element()` | Any collection | The collection element or map entry value
|
||||
| Always optional, and useful only to explicitly indicate intent.
|
||||
| `index()` | Any `List` with an index column | The index of the element in the list
|
||||
| For backward compatibility, it's also an alternative to ``key()``, when applied to a map.
|
||||
| `key()` | Any `Map` | The key of the entry in the list | If the key is of entity type, it may be further navigated.
|
||||
| `entry()` | Any `Map` | The map entry, that is, the `Map.Entry` of key and value.
|
||||
| Only legal as a terminal path, and only allowed in the `select` clause.
|
||||
|===
|
||||
A reference to a key, value, or entry of a <<hql-collection-valued-associations,joined map>>.
|
||||
|
||||
We've intentionally left two functions off this list, so we can come back to them <<hql-elements-indices,later>>.
|
||||
[[hql-collection-subquery]]
|
||||
===== `elements()`, and `indices()`
|
||||
|
||||
NOTE: Of these, only `index()` is defined by the JPQL specification.
|
||||
|
||||
[[hql-collection-qualification-example]]
|
||||
//.Qualified collection references example
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{modeldir}/Phone.java[tags=hql-collection-qualification-example, indent=0]
|
||||
|
||||
include::{sourcedir}/HQLTest.java[tags=hql-collection-qualification-example, indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
An element of an indexed collection (an array, list, or map) may even be identified using the index operator:
|
||||
|
||||
[[hql-collection-index-operator-example]]
|
||||
//.Index operator examples
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/HQLTest.java[tags=hql-collection-index-operator-example]
|
||||
----
|
||||
====
|
||||
Later, in <<hql-elements-indices>>, and in <<hql-aggregate-functions-collections>>, we will learn about these special functions for quantifying over the elements or indices of a particular collection.
|
||||
|
||||
[[hql-more-functions]]
|
||||
==== More HQL functions
|
||||
|
@ -1194,7 +1166,7 @@ As you can guess, `not like` and `not ilike` are the enemies of `like` and `ilik
|
|||
[[hql-elements-indices]]
|
||||
==== Elements and indices
|
||||
|
||||
There's two special HQL functions that we didn't mention <<hql-collection-qualification,earlier>>, since they're only useful in conjunction with the predicate operators we're about to meet.
|
||||
There's two special HQL functions that we mentioned <<hql-collection-subquery,earlier>>, without giving much of an explanation, since they're only useful in conjunction with the predicate operators we're about to meet.
|
||||
|
||||
These functions are only allowed in the `where` clause, and result in a subquery in the generated SQL.
|
||||
Indeed, you can think of them as just a shortcut way to write a subquery.
|
||||
|
@ -1673,9 +1645,13 @@ include::{sourcedir}/HQLTest.java[tags=hql-implicit-join-alias-example]
|
|||
====
|
||||
|
||||
[[hql-collection-valued-associations]]
|
||||
==== Collection member references
|
||||
==== Joining collections and many-valued associations
|
||||
|
||||
References to collection-valued associations actually refer to the _elements_ of that collection.
|
||||
When a join involves a collection or many-valued association, the declared identification variable refers to the _elements_ of the collection, that is:
|
||||
|
||||
- to the elements of a `Set`,
|
||||
- to the elements of a `List`, not to their indices in the list, or
|
||||
- to the values of a `Map`, not to their keys.
|
||||
|
||||
[[hql-collection-valued-associations-example]]
|
||||
//.Collection references example
|
||||
|
@ -1686,9 +1662,61 @@ include::{sourcedir}/HQLTest.java[tags=hql-collection-valued-associations]
|
|||
----
|
||||
====
|
||||
|
||||
In the example, the identification variable `ph` actually refers to the object model type `Phone`, which is the type of the elements of the `Person#phones` association.
|
||||
In this example, the identification variable `ph` is of type `Phone`, the element type of the list `Person#phones`.
|
||||
But if we need to refer to the index of a `Phone` in the list, we need some extra syntax.
|
||||
|
||||
You might recall that we mentioned <<hql-list-functions>> and <<hql-map-functions>> a bit earlier.
|
||||
These functions may be applied to the identification variable declared in a collection join or many-valued association join.
|
||||
|
||||
|===
|
||||
| Function | Applies to | Interpretation | Notes
|
||||
|
||||
| `value()` or `element()` | Any collection | The collection element or map entry value
|
||||
| Often optional.
|
||||
| `index()` | Any `List` with an index column | The index of the element in the list
|
||||
| For backward compatibility, it's also an alternative to ``key()``, when applied to a map.
|
||||
| `key()` | Any `Map` | The key of the entry in the list | If the key is of entity type, it may be further navigated.
|
||||
| `entry()` | Any `Map` | The map entry, that is, the `Map.Entry` of key and value.
|
||||
| Only legal as a terminal path, and only allowed in the `select` clause.
|
||||
|===
|
||||
|
||||
In particular, `index()` and `key()` obtain a reference to a list index or map key.
|
||||
|
||||
[[hql-collection-qualification-example]]
|
||||
//.Qualified collection references example
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{modeldir}/Phone.java[tags=hql-collection-qualification-example, indent=0]
|
||||
|
||||
include::{sourcedir}/HQLTest.java[tags=hql-collection-qualification-example, indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
[[hql-implicit-collection-join]]
|
||||
==== Implicit joins involving collections
|
||||
|
||||
The functions `element()`, `index()`, `key()`, and `value()` may even be applied to a path expression to express an implicit join.
|
||||
|
||||
[[hql-collection-implicit-join-example]]
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/HQLTest.java[tags=hql-collection-implicit-join-example, indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
An element of an indexed collection (an array, list, or map) may even be identified using the index operator:
|
||||
|
||||
[[hql-collection-index-operator-example]]
|
||||
//.Index operator examples
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/HQLTest.java[tags=hql-collection-index-operator-example]
|
||||
----
|
||||
====
|
||||
|
||||
But there _is_ a way to refer to the keys or indexes of a collection, as we've already seen in <<hql-collection-qualification>>.
|
||||
|
||||
[[hql-select-clause]]
|
||||
=== Projection: `select`
|
||||
|
|
|
@ -570,6 +570,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
//tag::hql-collection-qualification-example[]
|
||||
|
||||
// select all the calls (the map value) for a given Phone
|
||||
// note that here we don't need to use value() or element()
|
||||
// since it is implicit
|
||||
List<Call> calls = entityManager.createQuery(
|
||||
"select ch " +
|
||||
"from Phone ph " +
|
||||
|
@ -590,7 +592,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
Long id = 1L;
|
||||
//tag::hql-collection-qualification-example[]
|
||||
|
||||
// same as above
|
||||
// same as above, but with value() explicit
|
||||
List<Call> calls = entityManager.createQuery(
|
||||
"select value(ch) " +
|
||||
"from Phone ph " +
|
||||
|
@ -611,6 +613,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
//tag::hql-collection-qualification-example[]
|
||||
|
||||
// select all the Call timestamps (the map key) for a given Phone
|
||||
// note that here we *do* need to explicitly specify key()
|
||||
List<LocalDateTime> timestamps = entityManager.createQuery(
|
||||
"select key(ch) " +
|
||||
"from Phone ph " +
|
||||
|
@ -626,7 +629,6 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
|
||||
@Test
|
||||
public void test_hql_collection_qualification_associations_4() {
|
||||
try {
|
||||
doInJPA(this::entityManagerFactory, entityManager -> {
|
||||
|
||||
Long id = 1L;
|
||||
|
@ -643,9 +645,6 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
//end::hql-collection-qualification-example[]
|
||||
|
||||
});
|
||||
} catch(Exception e) {
|
||||
//@see https://hibernate.atlassian.net/browse/HHH-10491
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -674,6 +673,44 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_hql_collection_implicit_join_1() {
|
||||
doInJPA(this::entityManagerFactory, entityManager -> {
|
||||
Long id = 1L;
|
||||
//tag::hql-collection-implicit-join-example[]
|
||||
|
||||
// implicit join to a map value()
|
||||
List<Call> calls = entityManager.createQuery(
|
||||
"select value(ph.callHistory) " +
|
||||
"from Phone ph " +
|
||||
"where ph.id = :id ",
|
||||
Call.class)
|
||||
.setParameter("id", id)
|
||||
.getResultList();
|
||||
//end::hql-collection-implicit-join-example[]
|
||||
assertEquals(2, calls.size());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_hql_collection_implicit_join_2() {
|
||||
doInJPA(this::entityManagerFactory, entityManager -> {
|
||||
Long id = 1L;
|
||||
//tag::hql-collection-implicit-join-example[]
|
||||
|
||||
// implicit join to a map key()
|
||||
List<LocalDateTime> timestamps = entityManager.createQuery(
|
||||
"select key(ph.callHistory) " +
|
||||
"from Phone ph " +
|
||||
"where ph.id = :id ",
|
||||
LocalDateTime.class)
|
||||
.setParameter("id", id)
|
||||
.getResultList();
|
||||
//end::hql-collection-implicit-join-example[]
|
||||
assertEquals(2, timestamps.size());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_projection_example() {
|
||||
doInJPA(this::entityManagerFactory, entityManager -> {
|
||||
|
|
Loading…
Reference in New Issue