|
|
|
@ -15,49 +15,48 @@
|
|
|
|
|
Hibernate supports mapping collections (`java.util.Collection` and `java.util.Map` subtypes)
|
|
|
|
|
in a variety of ways.
|
|
|
|
|
|
|
|
|
|
TIP:: Hibernate even allows mapping a collection as `@Basic`, but that should generally be avoided.
|
|
|
|
|
Hibernate even allows mapping a collection as `@Basic`, but that should generally be avoided.
|
|
|
|
|
See <<collections-as-basic>> for details of such a mapping.
|
|
|
|
|
|
|
|
|
|
This section is limited to discussing `@ElementCollection`, `@OneToMany` and `@ManyToMany`.
|
|
|
|
|
|
|
|
|
|
Hibernate understands various aspects of collection mappings:
|
|
|
|
|
|
|
|
|
|
Semantics:: the type of collection (`List`, `Set`, etc.) and how Hibernate deals with it as a whole - <<collection-semantics>>
|
|
|
|
|
Parts:: the individual parts of the collection (element, e.g.) that map to the database - <<collection-parts>>
|
|
|
|
|
Nature:: whether the collection parts are considered elemental (`@ElementCollection`) or associative (`@OneToMany` and `@ManyToMany`) - <<collection-nature>>
|
|
|
|
|
[IMPORTANT]
|
|
|
|
|
====
|
|
|
|
|
Two entities cannot share a reference to the same collection instance.
|
|
|
|
|
|
|
|
|
|
IMPORTANT:: Collections mapped by Hibernate cannot be nested (`List<List<?>>`), When used in collections, embeddable types cannot define plural attributes.
|
|
|
|
|
Collection-valued properties do not support null value semantics.
|
|
|
|
|
|
|
|
|
|
Hibernate provides its own implementation of the collection types (plus array mappings) for various
|
|
|
|
|
purposes. See <<collection-wrapper>> for details.
|
|
|
|
|
Collections cannot be nested, meaning Hibernate does not support mapping `List<List<?>>`, for example.
|
|
|
|
|
|
|
|
|
|
[[collection-semantics]]
|
|
|
|
|
==== Semantics
|
|
|
|
|
Embeddables which are used as a collection element, Map value or Map key may not themselves define collections
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
The semantics of a collection include the specific collection subtype to use as well
|
|
|
|
|
as how to handle the collection as a whole. This breaks down based on the classification
|
|
|
|
|
as represented by `org.hibernate.metamodel.CollectionClassification`, which indicates the
|
|
|
|
|
`org.hibernate.collection.spi.CollectionSemantics` to use. `CollectionSemantics` exposes
|
|
|
|
|
the capabilities of the mapping such as
|
|
|
|
|
|
|
|
|
|
[[collections-semantics]]
|
|
|
|
|
==== Collection Semantics
|
|
|
|
|
|
|
|
|
|
The semantics of a collection describes how to handle the collection, including
|
|
|
|
|
|
|
|
|
|
* the collection subtype to use - `java.util.List`, `java.util.Set`, `java.util.SortedSet`, etc.
|
|
|
|
|
* how to access elements of the collection
|
|
|
|
|
* how to create instances of the collection - both "raw" and "wrapper" forms. See <<collection-wrapper>>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Hibernate supports the following classifications:
|
|
|
|
|
Hibernate supports the following semantics:
|
|
|
|
|
|
|
|
|
|
ARRAY:: Object and primitive arrays. See <<collection-array>>.
|
|
|
|
|
BAG:: A collection that may contain duplicate entries and has no defined ordering. See <<collection-bag>>.
|
|
|
|
|
ID_BAG:: A bag that defines a per-element identifier to uniquely identify elements in the collection. See <<collection-bag>>.
|
|
|
|
|
LIST:: Follows the semantics defined by `java.util.List`. See <<collection-list>>.
|
|
|
|
|
SET:: Follows the semantics defined by `java.util.Set`. See <<collection-set>>.
|
|
|
|
|
ORDERED_SET:: A set that is ordered by a SQL fragment defined on its mapping. See <<collection-set>>.
|
|
|
|
|
SORTED_SET:: A set that is sorted according to a `Comparator` defined on its mapping. See <<collection-set>>.
|
|
|
|
|
MAP:: Follows the semantics defined by `java.util.Map`. See <<collection-map>>.
|
|
|
|
|
ORDERED_MAP:: A map that is ordered by keys according to a SQL fragment defined on its mapping. See <<collection-map>>.
|
|
|
|
|
SORTED_MAP:: A map that is sorted by keys according to a `Comparator` defined on its mapping. See <<collection-map>>.
|
|
|
|
|
ARRAY:: Object and primitive arrays. See <<collections-array>>.
|
|
|
|
|
BAG:: A collection that may contain duplicate entries and has no defined ordering. See <<collections-bag>>.
|
|
|
|
|
ID_BAG:: A bag that defines a per-element identifier to uniquely identify elements in the collection. See <<collections-bag>>.
|
|
|
|
|
LIST:: Follows the semantics defined by `java.util.List`. See <<collections-list>>.
|
|
|
|
|
SET:: Follows the semantics defined by `java.util.Set`. See <<collections-set>>.
|
|
|
|
|
ORDERED_SET:: A set that is ordered by a SQL fragment defined on its mapping. See <<collections-set>>.
|
|
|
|
|
SORTED_SET:: A set that is sorted according to a `Comparator` defined on its mapping. See <<collections-set>>.
|
|
|
|
|
MAP:: Follows the semantics defined by `java.util.Map`. See <<collections-map>>.
|
|
|
|
|
ORDERED_MAP:: A map that is ordered by keys according to a SQL fragment defined on its mapping. See <<collections-map>>.
|
|
|
|
|
SORTED_MAP:: A map that is sorted by keys according to a `Comparator` defined on its mapping. See <<collections-map>>.
|
|
|
|
|
|
|
|
|
|
By default, Hiberate interprets the defined type of the plural attribute and makes an
|
|
|
|
|
By default, Hibernate interprets the defined type of the plural attribute and makes an
|
|
|
|
|
interpretation as to which classification it fits in to, using the following checks:
|
|
|
|
|
|
|
|
|
|
1. if an array -> ARRAY
|
|
|
|
@ -69,81 +68,6 @@ interpretation as to which classification it fits in to, using the following che
|
|
|
|
|
7. else `Collection` -> BAG
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The following sections related to mapping the various classifications all use simple
|
|
|
|
|
"element collections" for their examples. Element collections versus association
|
|
|
|
|
collections are discussed later in <<collection-nature>>. The actual classification
|
|
|
|
|
configuration is the same between them. The examples map collections of a `Name` type:
|
|
|
|
|
|
|
|
|
|
[[collection-name-ex]]
|
|
|
|
|
.Name class
|
|
|
|
|
====
|
|
|
|
|
[source, JAVA, indent=0]
|
|
|
|
|
----
|
|
|
|
|
include::{classificationTestsDir}/Name.java[tags=collections-name-ex]
|
|
|
|
|
----
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[collection-set]]
|
|
|
|
|
==== Mapping Sets
|
|
|
|
|
|
|
|
|
|
`java.util.Set` defines a collection of unique, though unordered elements. Hibernate supports
|
|
|
|
|
mapping sets according to the requirements of the `java.util.Set`.
|
|
|
|
|
|
|
|
|
|
[[collection-set-ex]]
|
|
|
|
|
.Basic Set Mapping
|
|
|
|
|
====
|
|
|
|
|
[source, JAVA, indent=0]
|
|
|
|
|
----
|
|
|
|
|
include::{classificationTestsDir}/set/EntityWithSet.java[tags=collections-set-ex]
|
|
|
|
|
----
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
Hibernate also has the ability to map sorted and ordered sets. A sorted set orders its
|
|
|
|
|
elements in memory via an associated `Comparator`; an ordered set is ordered via
|
|
|
|
|
SQL when the set is loaded.
|
|
|
|
|
|
|
|
|
|
TIP:: An ordered set does not perform any sorting in-memory. If an element is added
|
|
|
|
|
after the collection is loaded, the collection would need to be refreshed to re-order
|
|
|
|
|
the elements. For this reason, ordered sets are not recommended - if the application
|
|
|
|
|
needs ordering of the set elements, a sorted set should be preferred. For this reason,
|
|
|
|
|
it is not covered in the User Guide. See the javadocs for `jakarta.persistence.OrderBy`
|
|
|
|
|
or `org.hibernate.annotations.OrderBy` for details.
|
|
|
|
|
|
|
|
|
|
There are 2 options for sorting a set - naturally or using an explicit comparator.
|
|
|
|
|
|
|
|
|
|
A set is naturally sorted using the natural sort comparator for its elements. Generally
|
|
|
|
|
this implies that the element type is `Comparable`. E.g.
|
|
|
|
|
|
|
|
|
|
[[collection-sortedset-natural-ex]]
|
|
|
|
|
.@SortNatural
|
|
|
|
|
====
|
|
|
|
|
[source, JAVA, indent=0]
|
|
|
|
|
----
|
|
|
|
|
include::{classificationTestsDir}/set/EntityWithNaturallySortedSet.java[tags=collections-sortedset-natural-ex]
|
|
|
|
|
----
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
Because `Name` is defined as `Comparable`, its `#compare` method will be used to sort the elements in this
|
|
|
|
|
set.
|
|
|
|
|
|
|
|
|
|
But Hibernate also allows sorting based on a specific `Comparator` implementation. Here, e.g., we map
|
|
|
|
|
the `Names` as sorted by a `NameComparator`:
|
|
|
|
|
|
|
|
|
|
[[collection-sortedset-comparator-ex]]
|
|
|
|
|
.@SortComparator
|
|
|
|
|
====
|
|
|
|
|
[source, JAVA, indent=0]
|
|
|
|
|
----
|
|
|
|
|
include::{classificationTestsDir}/NameComparator.java[tags=collections-name-comparator-ex]
|
|
|
|
|
|
|
|
|
|
include::{classificationTestsDir}/set/EntityWithSortedSet.java[tags=collections-sortedset-comparator-ex]
|
|
|
|
|
----
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
Here, instead of `Name#compare` being use for the sorting, the explicit `NameComparator` will be used
|
|
|
|
|
instead.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[collection-list]]
|
|
|
|
|
==== Mapping Lists
|
|
|
|
@ -188,7 +112,7 @@ include::{classificationTestsDir}/list/EntityWithOrderColumnList.java[tags=colle
|
|
|
|
|
Now, a column named `name_index` will be used.
|
|
|
|
|
|
|
|
|
|
Hibernate stores index values into the order-column based on the element's position in the list
|
|
|
|
|
with no adjustment. The element at `names[0]` is stored with `name_position=0` and so on. That is to say
|
|
|
|
|
with no adjustment. The element at `names[0]` is stored with `name_index=0` and so on. That is to say
|
|
|
|
|
that the list index is considered 0-based just as list indexes themselves are 0-based. Some legacy
|
|
|
|
|
schemas might map the position as 1-based, or any base really. Hibernate also defines support for such
|
|
|
|
|
cases using its `@ListIndexBase` annotation.
|
|
|
|
@ -204,13 +128,89 @@ include::{classificationTestsDir}/list/EntityWithIndexBasedList.java[tags=collec
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[collection-map]]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[collections-set]]
|
|
|
|
|
==== Mapping Sets
|
|
|
|
|
|
|
|
|
|
`java.util.Set` defines a collection of unique, though unordered elements. Hibernate supports
|
|
|
|
|
mapping sets according to the requirements of the `java.util.Set`.
|
|
|
|
|
|
|
|
|
|
[[collection-set-ex]]
|
|
|
|
|
.Basic Set Mapping
|
|
|
|
|
====
|
|
|
|
|
[source, JAVA, indent=0]
|
|
|
|
|
----
|
|
|
|
|
include::{classificationTestsDir}/set/EntityWithSet.java[tags=collections-set-ex]
|
|
|
|
|
----
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
Hibernate also has the ability to map sorted and ordered sets. A sorted set orders its
|
|
|
|
|
elements in memory via an associated `Comparator`; an ordered set is ordered via
|
|
|
|
|
SQL when the set is loaded.
|
|
|
|
|
|
|
|
|
|
TIP:: An ordered set does not perform any sorting in-memory. If an element is added
|
|
|
|
|
after the collection is loaded, the collection would need to be refreshed to re-order
|
|
|
|
|
the elements. For this reason, ordered sets are not recommended - if the application
|
|
|
|
|
needs ordering of the set elements, a sorted set should be preferred. For this reason,
|
|
|
|
|
it is not covered in the User Guide. See the javadocs for `jakarta.persistence.OrderBy`
|
|
|
|
|
or `org.hibernate.annotations.OrderBy` for details.
|
|
|
|
|
|
|
|
|
|
There are 2 options for sorting a set - naturally or using an explicit comparator.
|
|
|
|
|
|
|
|
|
|
A set is naturally sorted using the natural sort comparator for its elements. Generally
|
|
|
|
|
this implies that the element type is `Comparable`. E.g.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[collection-sortedset-natural-ex]]
|
|
|
|
|
.@SortNatural
|
|
|
|
|
====
|
|
|
|
|
[source, JAVA, indent=0]
|
|
|
|
|
----
|
|
|
|
|
include::{classificationTestsDir}/Name.java[tags=collections-name-ex]
|
|
|
|
|
|
|
|
|
|
include::{classificationTestsDir}/set/EntityWithNaturallySortedSet.java[tags=collections-sortedset-natural-ex]
|
|
|
|
|
----
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
Because `Name` is defined as `Comparable`, its `#compare` method will be used to sort the elements in this
|
|
|
|
|
set.
|
|
|
|
|
|
|
|
|
|
But Hibernate also allows sorting based on a specific `Comparator` implementation. Here, e.g., we map
|
|
|
|
|
the `Names` as sorted by a `NameComparator`:
|
|
|
|
|
|
|
|
|
|
[[collection-sortedset-comparator-ex]]
|
|
|
|
|
.@SortComparator
|
|
|
|
|
====
|
|
|
|
|
[source, JAVA, indent=0]
|
|
|
|
|
----
|
|
|
|
|
include::{classificationTestsDir}/NameComparator.java[tags=collections-name-comparator-ex]
|
|
|
|
|
|
|
|
|
|
include::{classificationTestsDir}/set/EntityWithSortedSet.java[tags=collections-sortedset-comparator-ex]
|
|
|
|
|
----
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
Here, instead of `Name#compare` being use for the sorting, the explicit `NameComparator` will be used
|
|
|
|
|
instead.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[collections-map]]
|
|
|
|
|
==== Mapping Maps
|
|
|
|
|
|
|
|
|
|
// todo (6.0) - finish
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[collection-bag]]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[collections-bag]]
|
|
|
|
|
==== Mapping Collections
|
|
|
|
|
|
|
|
|
|
Without any other mapping influencers, `java.util.Collection` is interpreted using BAG
|
|
|
|
@ -259,7 +259,9 @@ table. `@CollectionId` is the annotation to configure this identifier
|
|
|
|
|
// todo (6.0) - finish
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[collection-array]]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[collections-array]]
|
|
|
|
|
==== Mapping Arrays
|
|
|
|
|
|
|
|
|
|
Hibernate is able to map Object and primitive arrays as collections. Mapping an array is essentially
|
|
|
|
@ -274,36 +276,17 @@ these would be mapped as binary data.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[collection-nature]]
|
|
|
|
|
==== Nature
|
|
|
|
|
|
|
|
|
|
The elements of a collection may either be value types (basic or embedded) or associations to
|
|
|
|
|
entities.
|
|
|
|
|
|
|
|
|
|
Collections of basic- or embedded-valued elements are called element collections. They are mapped
|
|
|
|
|
using the `jakarta.persistence.ElementCollection` annotation.
|
|
|
|
|
|
|
|
|
|
Collections of entity-valued elements are called association collections. They are mapped using
|
|
|
|
|
either the `jakarta.persistence.OneToMany` or `jakarta.persistence.ManyToMany` annotations.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[IMPORTANT]
|
|
|
|
|
====
|
|
|
|
|
Two entities cannot share a reference to the same collection instance.
|
|
|
|
|
Collection-valued properties do not support null value semantics because Hibernate does not distinguish between a null collection reference and an empty collection.
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
// todo (6.0) - finish
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[collection-elemental]]
|
|
|
|
|
===== @ElementCollection
|
|
|
|
|
[[collections-elemental]]
|
|
|
|
|
==== @ElementCollection
|
|
|
|
|
|
|
|
|
|
Element collections may contain values of either basic or embeddable types. They have a similar
|
|
|
|
|
lifecycle to basic/embedded attributes in that their persistence is completely managed as part of
|
|
|
|
|
the owner - they are created when referenced from an owner and automatically deleted when
|
|
|
|
|
unreferenced. The specifics of how this lifecycle manifests in terms of database calls depends
|
|
|
|
|
on the <<collection-semantics,semantics>> of the mapping.
|
|
|
|
|
on the <<collections-semantics,semantics>> of the mapping.
|
|
|
|
|
|
|
|
|
|
This section will discuss these lifecycle aspects using the example of mapping a collection
|
|
|
|
|
of phone numbers. The examples use embeddable values, but the same aspects apply to collections
|
|
|
|
@ -348,6 +331,14 @@ include::{extrasdir}/elemental-bag-lifecycle-example.sql[]
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// todo (6.0) - finish
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[collection-nature-entity]]
|
|
|
|
@ -367,17 +358,8 @@ A bidirectional association has an _owning_ side and an _inverse (mappedBy)_ sid
|
|
|
|
|
// todo (6.0) - finish
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[collection-parts]]
|
|
|
|
|
==== Parts
|
|
|
|
|
|
|
|
|
|
A collection mapping includes several parts:
|
|
|
|
|
|
|
|
|
|
Key:: The foreign-key that associates the collection with it owner
|
|
|
|
|
Element:: The element of the collection (or value of a Map)
|
|
|
|
|
Index:: the index of the collection/array (or key of a Map)
|
|
|
|
|
Id:: the identifier for an ID_BAG
|
|
|
|
|
|
|
|
|
|
// todo (6.0) - finish
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -426,22 +408,30 @@ include::{coreCollectionTestsDir}/semantics/UniqueListWrapper.java[tags=collecti
|
|
|
|
|
`UniqueListWrapper` is the `PersistentCollection` implementation for the "unique list" semantic. See <<collection-wrapper>> for more details.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
For cases where an application wants to apply the same custom type to all
|
|
|
|
|
plural attributes of a given classification, Hibernate also provides the
|
|
|
|
|
`@CollectionTypeRegistration`:
|
|
|
|
|
|
|
|
|
|
// todo (6.0) - example of using `@CollectionTypeRegistration`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[collection-type-reg-ann]]
|
|
|
|
|
==== @CollectionTypeRegistration
|
|
|
|
|
|
|
|
|
|
For cases where an application wants to apply the same custom type to all
|
|
|
|
|
plural attributes of a given classification, Hibernate also provides the
|
|
|
|
|
`@CollectionTypeRegistration`:
|
|
|
|
|
|
|
|
|
|
[[collection-type-usertype-registration-ex]]
|
|
|
|
|
.UniqueListType Registration
|
|
|
|
|
====
|
|
|
|
|
[source, JAVA, indent=0]
|
|
|
|
|
----
|
|
|
|
|
include::{coreCollectionTestsDir}/semantics/TheEntityWithUniqueListRegistration.java[tags=ex-collections-custom-type-model]
|
|
|
|
|
----
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
This example behaves exactly as in <<collection-type-ann-ex>>.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[collection-wrapper]]
|
|
|
|
|
==== Wrappers
|
|
|
|
|
|
|
|
|
|
As mentioned in <<collection-semantics>>, Hibernate provides its own implementations
|
|
|
|
|
As mentioned in <<collections-semantics>>, Hibernate provides its own implementations
|
|
|
|
|
of the Java collection types. These are called wrappers as they wrap an underlying
|
|
|
|
|
collection and provide support for things like lazy loading, queueing add/remove
|
|
|
|
|
operations while detached, etc. Hibernate defines the following `PersistentCollection`
|
|
|
|
@ -462,8 +452,6 @@ The collections they wrap are called "raw" collections, which are generally the
|
|
|
|
|
Java implementations (`java.util.ArrayList`, etc)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// todo (6.0) - finish
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|