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`].
|
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]]
|
[[hql-list-functions]]
|
||||||
==== Collection elements, map keys, and list indexes
|
===== `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>>.
|
||||||
|
|
||||||
|===
|
[[hql-map-functions]]
|
||||||
| Function | Applies to | Interpretation | Notes
|
===== `key()`, `value()`, and `entry()`
|
||||||
|
|
||||||
| `value()` or `element()` | Any collection | The collection element or map entry value
|
A reference to a key, value, or entry of a <<hql-collection-valued-associations,joined map>>.
|
||||||
| 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.
|
|
||||||
|===
|
|
||||||
|
|
||||||
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.
|
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-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]
|
|
||||||
----
|
|
||||||
====
|
|
||||||
|
|
||||||
[[hql-more-functions]]
|
[[hql-more-functions]]
|
||||||
==== More HQL 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]]
|
[[hql-elements-indices]]
|
||||||
==== Elements and 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.
|
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.
|
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]]
|
[[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]]
|
[[hql-collection-valued-associations-example]]
|
||||||
//.Collection references 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]]
|
[[hql-select-clause]]
|
||||||
=== Projection: `select`
|
=== Projection: `select`
|
||||||
|
|
|
@ -570,6 +570,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
//tag::hql-collection-qualification-example[]
|
//tag::hql-collection-qualification-example[]
|
||||||
|
|
||||||
// select all the calls (the map value) for a given Phone
|
// 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(
|
List<Call> calls = entityManager.createQuery(
|
||||||
"select ch " +
|
"select ch " +
|
||||||
"from Phone ph " +
|
"from Phone ph " +
|
||||||
|
@ -590,7 +592,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
Long id = 1L;
|
Long id = 1L;
|
||||||
//tag::hql-collection-qualification-example[]
|
//tag::hql-collection-qualification-example[]
|
||||||
|
|
||||||
// same as above
|
// same as above, but with value() explicit
|
||||||
List<Call> calls = entityManager.createQuery(
|
List<Call> calls = entityManager.createQuery(
|
||||||
"select value(ch) " +
|
"select value(ch) " +
|
||||||
"from Phone ph " +
|
"from Phone ph " +
|
||||||
|
@ -611,6 +613,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
//tag::hql-collection-qualification-example[]
|
//tag::hql-collection-qualification-example[]
|
||||||
|
|
||||||
// select all the Call timestamps (the map key) for a given Phone
|
// 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(
|
List<LocalDateTime> timestamps = entityManager.createQuery(
|
||||||
"select key(ch) " +
|
"select key(ch) " +
|
||||||
"from Phone ph " +
|
"from Phone ph " +
|
||||||
|
@ -626,7 +629,6 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_hql_collection_qualification_associations_4() {
|
public void test_hql_collection_qualification_associations_4() {
|
||||||
try {
|
|
||||||
doInJPA(this::entityManagerFactory, entityManager -> {
|
doInJPA(this::entityManagerFactory, entityManager -> {
|
||||||
|
|
||||||
Long id = 1L;
|
Long id = 1L;
|
||||||
|
@ -643,9 +645,6 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
//end::hql-collection-qualification-example[]
|
//end::hql-collection-qualification-example[]
|
||||||
|
|
||||||
});
|
});
|
||||||
} catch(Exception e) {
|
|
||||||
//@see https://hibernate.atlassian.net/browse/HHH-10491
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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
|
@Test
|
||||||
public void test_projection_example() {
|
public void test_projection_example() {
|
||||||
doInJPA(this::entityManagerFactory, entityManager -> {
|
doInJPA(this::entityManagerFactory, entityManager -> {
|
||||||
|
|
Loading…
Reference in New Issue