UserGuide uses examples from existing test cases

This commit is contained in:
vladmihalcea 2016-01-18 15:18:23 +02:00
parent 2bda2515af
commit 97ba351c2a
144 changed files with 452 additions and 2322 deletions

View File

@ -1,6 +1,6 @@
[[associations]]
=== Associations
:sourcedir: extras
:sourcedir: ../../../../../test/java/org/hibernate/jpa/test/userguide/associations
Associations describe how two or more entities form a relationship based on a database joining semantics.
@ -10,31 +10,33 @@ Associations describe how two or more entities form a relationship based on a da
`@ManyToOne` is the most common association, having a direct equivalent in the relational database as well (e.g. foreign key),
and so it establishes a relationship between a child entity and a parent.
[[associations-many-to-one-example]]
.`@ManyToOne` association
====
[source,java]
----
include::{sourcedir}/associations/ManyToOne.java[]
include::{sourcedir}/ManyToOneTest.java[tags=associations-many-to-one-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/associations/ManyToOne.sql[]
include::{sourcedir}/associations-many-to-one-example.sql[]
----
====
Each entity has a lifecycle of its own. Once the `@ManyToOne` association is set, Hibernate will set the associated database foreign key column.
[[associations-many-to-one-lifecycle-example]]
.`@ManyToOne` association lifecycle
====
[source,java]
----
include::{sourcedir}/associations/ManyToOneLifecycle.java[]
include::{sourcedir}/ManyToOneTest.java[tags=associations-many-to-one-lifecycle-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/associations/ManyToOneLifecycle.sql[]
include::{sourcedir}/associations-many-to-one-lifecycle-example.sql[]
----
====
@ -50,16 +52,17 @@ If there is a `@ManyToOne` association on the child side, the `@OneToMany` assoc
When using a unidirectional `@OneToMany` association, Hibernate resorts to using a link table between the two joining entities.
[[associations-one-to-many-unidirectional-example]]
.Unidirectional `@OneToMany` association
====
[source,java]
----
include::{sourcedir}/associations/UnidirectionalOneToMany.java[]
include::{sourcedir}/OneToManyUnidirectionalTest.java[tags=associations-one-to-many-unidirectional-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/associations/UnidirectionalOneToMany.sql[]
include::{sourcedir}/associations-one-to-many-unidirectional-example.sql[]
----
====
@ -69,16 +72,17 @@ The `@OneToMany` association is by definition a parent association, even if it's
Only the parent side of an association makes sense to cascade its entity state transitions to children.
====
[[associations-one-to-many-unidirectional-lifecycle-example]]
.Cascading `@OneToMany` association
====
[source,java]
----
include::{sourcedir}/associations/UnidirectionalOneToManyLifecycle.java[]
include::{sourcedir}/OneToManyUnidirectionalTest.java[tags=associations-one-to-many-unidirectional-lifecycle-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/associations/UnidirectionalOneToManyLifecycle.sql[]
include::{sourcedir}/associations-one-to-many-unidirectional-lifecycle-example.sql[]
----
====
@ -101,16 +105,17 @@ Although the Domain Model exposes two sides to navigate this association, behind
Every bidirectional association must have one owning side only (the child side), the other one being referred to as the _inverse_ (or the `mappedBy`) side.
[[associations-one-to-many-bidirectional-example]]
.`@OneToMany` association mappedBy the `@ManyToOne` side
====
[source,java]
----
include::{sourcedir}/associations/BidirectionalOneToMany.java[]
include::{sourcedir}/OneToManyBidirectionalTest.java[tags=associations-one-to-many-bidirectional-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/associations/BidirectionalOneToMany.sql[]
include::{sourcedir}/associations-one-to-many-bidirectional-example.sql[]
----
====
@ -123,16 +128,17 @@ The `addPhone()` and `removePhone()` are utilities methods that synchronize both
Because the `Phone` class has a `@NaturalId` column (the phone number being unique),
the `equals()` and the `hashCode()` can make use of this property, and so the `removePhone()` logic is reduced to the `remove()` Java `Collection` method.
[[associations-one-to-many-bidirectional-lifecycle-example]]
.Bidirectional `@OneToMany` with an owner `@ManyToOne` side lifecycle
====
[source,java]
----
include::{sourcedir}/associations/BidirectionalOneToManyLifecycle.java[]
include::{sourcedir}/OneToManyBidirectionalTest.java[tags=associations-one-to-many-bidirectional-lifecycle-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/associations/BidirectionalOneToManyLifecycle.sql[]
include::{sourcedir}/associations-one-to-many-bidirectional-lifecycle-example.sql[]
----
====
@ -151,16 +157,17 @@ A bidirectional association features a `mappedBy` `@OneToOne` parent side too.
[[associations-one-to-one-unidirectional]]
===== Unidirectional `@OneToOne`
[[associations-one-to-one-unidirectional-example]]
.Unidirectional `@OneToOne`
====
[source,java]
----
include::{sourcedir}/associations/UnidirectionalOneToOne.java[]
include::{sourcedir}/OneToOneUnidirectionalTest.java[tags=associations-one-to-one-unidirectional-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/associations/UnidirectionalOneToOne.sql[]
include::{sourcedir}/associations-one-to-one-unidirectional-example.sql[]
----
====
@ -174,42 +181,46 @@ This mapping requires a bidirectional `@OneToOne` association as you can see in
[[associations-one-to-one-bidirectional]]
===== Bidirectional `@OneToOne`
[[associations-one-to-one-bidirectional-example]]
.Bidirectional `@OneToOne`
====
[source,java]
----
include::{sourcedir}/associations/BidirectionalOneToOne.java[]
include::{sourcedir}/OneToOneBidirectionalTest.java[tags=associations-one-to-one-bidirectional-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/associations/BidirectionalOneToOne.sql[]
include::{sourcedir}/associations-one-to-one-bidirectional-example.sql[]
----
====
This time, the `PhoneDetails` owns the association, and, like any bidirectional association, the parent-side can propagate its lifecycle to the child-side through cascading.
[[associations-one-to-one-bidirectional-lifecycle-example]]
.Bidirectional `@OneToOne` lifecycle
====
[source,java]
----
include::{sourcedir}/associations/BidirectionalOneToOneLifecycle.java[]
include::{sourcedir}/OneToOneBidirectionalTest.java[tags=associations-one-to-one-bidirectional-lifecycle-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/associations/BidirectionalOneToOneLifecycle.sql[]
include::{sourcedir}/associations-one-to-one-bidirectional-lifecycle-example.sql[]
----
====
When using a bidirectional `@OneToOne` association, Hibernate enforces the unique constraint upon fetching the child-side.
If there are more than one children associated to the same parent, Hibernate will throw a constraint violation exception.
Continuing the previous example, when adding another `PhoneDetails`, Hibernate validates the uniqueness constraint when reloading the `Phone` object.
[[associations-one-to-one-bidirectional-constraint-example]]
.Bidirectional `@OneToOne` unique constraint
====
[source,java]
----
include::{sourcedir}/associations/BidirectionalOneToOneConstraint.java[]
include::{sourcedir}/OneToOneBidirectionalTest.java[tags=associations-one-to-one-bidirectional-constraint-example,indent=0]
----
====
@ -222,16 +233,17 @@ Like the `@OneToMany` association, `@ManyToMany` can be a either unidirectional
[[associations-many-to-many-unidirectional]]
===== Unidirectional `@ManyToMany`
[[associations-many-to-many-unidirectional-example]]
.Unidirectional `@ManyToMany`
====
[source,java]
----
include::{sourcedir}/associations/UnidirectionalManyToMany.java[]
include::{sourcedir}/ManyToManyUnidirectionalTest.java[tags=associations-many-to-many-unidirectional-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/associations/UnidirectionalManyToMany.sql[]
include::{sourcedir}/associations-many-to-many-unidirectional-example.sql[]
----
====
@ -240,16 +252,17 @@ Just like with unidirectional `@OneToMany` associations, the link table is contr
When an entity is removed from the `@ManyToMany` collection, Hibernate simply deletes the joining record in the link table.
Unfortunately, this operation requires removing all entries associated to a given parent and recreating the ones that are listed in the current running persistent context.
[[associations-many-to-many-unidirectional-lifecycle-example]]
.Unidirectional `@ManyToMany` lifecycle
====
[source,java]
----
include::{sourcedir}/associations/UnidirectionalManyToManyLifecycle.java[]
include::{sourcedir}/ManyToManyUnidirectionalTest.java[tags=associations-many-to-many-unidirectional-lifecycle-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/associations/UnidirectionalManyToManyLifecycle.sql[]
include::{sourcedir}/associations-many-to-many-unidirectional-lifecycle-example.sql[]
----
====
@ -274,16 +287,17 @@ Caused by: java.sql.SQLIntegrityConstraintViolationException: integrity constrai
By simply removing the parent-side, Hibernate can safely remove the associated link records as you can see in the following example:
[[associations-many-to-many-unidirectional-remove-example]]
.Unidirectional `@ManyToMany` entity removal
====
[source,java]
----
include::{sourcedir}/associations/UnidirectionalManyToManyRemove.java[]
include::{sourcedir}/ManyToManyUnidirectionalTest.java[tags=associations-many-to-many-unidirectional-remove-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/associations/UnidirectionalManyToManyRemove.sql[]
include::{sourcedir}/associations-many-to-many-unidirectional-remove-example.sql[]
----
====
@ -293,31 +307,33 @@ include::{sourcedir}/associations/UnidirectionalManyToManyRemove.sql[]
A bidirectional `@ManyToMany` association has an owning and a `mappedBy` side.
To preserve synchronicity between both sides, it's good practice to provide helper methods for adding or removing child entities.
[[associations-many-to-many-bidirectional-example]]
.Bidirectional `@ManyToMany`
====
[source,java]
----
include::{sourcedir}/associations/BidirectionalManyToMany.java[]
include::{sourcedir}/ManyToManyBidirectionalTest.java[tags=associations-many-to-many-bidirectional-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/associations/BidirectionalManyToMany.sql[]
include::{sourcedir}/associations-many-to-many-bidirectional-example.sql[]
----
====
With the helper methods in place, the synchronicity management can be simplified, as you can see in the following example:
[[associations-many-to-many-bidirectional-lifecycle-example]]
.Bidirectional `@ManyToMany` lifecycle
====
[source,java]
----
include::{sourcedir}/associations/BidirectionalManyToManyLifecycle.java[]
include::{sourcedir}/ManyToManyBidirectionalTest.java[tags=associations-many-to-many-bidirectional-lifecycle-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/associations/BidirectionalManyToManyLifecycle.sql[]
include::{sourcedir}/associations-many-to-many-bidirectional-lifecycle-example.sql[]
----
====
@ -331,16 +347,17 @@ To overcome this limitation, the the link table must be directly exposed and the
To most natural `@ManyToMany` association follows the same logic employed by the database schema,
and the link table has an associated entity which controls the relationship for both sides that need to be joined.
[[associations-many-to-many-bidirectional-with-link-entity-example]]
.Bidirectional many-to-many with link entity
====
[source,java]
----
include::{sourcedir}/associations/BidirectionalManyToManyWithLinkEntity.java[]
include::{sourcedir}/ManyToManyBidirectionalWithLinkEntityTest.java[tags=associations-many-to-many-bidirectional-with-link-entity-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/associations/BidirectionalManyToManyWithLinkEntity.sql[]
include::{sourcedir}/associations-many-to-many-bidirectional-with-link-entity-example.sql[]
----
====
@ -355,16 +372,17 @@ For more details, see the <<chapters/domain/identifiers.adoc#identifiers-composi
The entity state transitions are better managed than in the previous bidirectional `@ManyToMany` case.
[[associations-many-to-many-bidirectional-with-link-entity-lifecycle-example]]
.Bidirectional many-to-many with link entity lifecycle
====
[source,java]
----
include::{sourcedir}/associations/BidirectionalManyToManyWithLinkEntityLifecycle.java[]
include::{sourcedir}/ManyToManyBidirectionalWithLinkEntityTest.java[tags=associations-many-to-many-bidirectional-with-link-entity-lifecycle-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/associations/BidirectionalManyToManyWithLinkEntityLifecycle.sql[]
include::{sourcedir}/associations-many-to-many-bidirectional-with-link-entity-lifecycle-example.sql[]
----
====

View File

@ -1,6 +1,6 @@
[[collections]]
=== Collections
:sourcedir: extras
:sourcedir: ../../../../../test/java/org/hibernate/jpa/test/userguide/collections
Naturally Hibernate also allows to persist collections.
These persistent collections can contain almost any other Hibernate type, including: basic types, custom types, components and references to other entities.
@ -22,11 +22,14 @@ The actual interface might be `java.util.Collection`, `java.util.List`, `java.ut
As the following example demonstrates, it's important to use the interface type and not the collection implementation, as declared in the entity mapping.
[[collections-collection-proxy-example]]
.Hibernate uses its own collection implementations
====
[source,java]
----
include::{sourcedir}/collections/CollectionProxy.java[]
include::{sourcedir}/BasicTypeElementCollectionTest.java[tags=collections-collection-proxy-entity-example,indent=0]
include::{sourcedir}/BasicTypeElementCollectionTest.java[tags=collections-collection-proxy-usage-example,indent=0]
----
====
@ -63,47 +66,52 @@ The lifecycle of the value-type collection is entirely controlled by its owning
Considering the previous example mapping, when clearing the phone collection, Hibernate deletes all the associated phones.
When adding a new element to the value type collection, Hibernate issues a new insert statement.
[[collections-value-type-collection-lifecycle-example]]
.Value type collection lifecycle
====
[source,java]
----
include::{sourcedir}/collections/ElementCollectionLifecycle.java[]
include::{sourcedir}/BasicTypeElementCollectionTest.java[tags=collections-value-type-collection-lifecycle-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/collections/ElementCollectionLifecycle.sql[]
include::{sourcedir}/collections-value-type-collection-lifecycle-example.sql[]
----
====
If removing all elements or adding new ones is rather straightforward, removing a certain entry actually requires reconstructing the whole collection from scratch.
[[collections-value-type-collection-remove-example]]
.Removing collection elements
====
[source,java]
----
include::{sourcedir}/collections/ElementCollectionLifecycleRemove.java[]
include::{sourcedir}/BasicTypeElementCollectionTest.java[tags=collections-value-type-collection-remove-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/collections/ElementCollectionLifecycleRemove.sql[]
include::{sourcedir}/collections-value-type-collection-remove-example.sql[]
----
====
Depending on the number of elements, this behavior might not be efficient, if many elements need to be deleted and reinserted back into the database table.
A workaround is to use an `@OrderColumn`, which, although not as efficient as when using the actual link table primary key, might improve the efficiency of the remove operations.
[[collections-value-type-collection-order-column-remove-example]]
.Removing collection elements using the order column
====
[source,java]
----
include::{sourcedir}/collections/ElementCollectionOrderColumnLifecycleRemove.java[]
include::{sourcedir}/BasicTypeOrderColumnElementCollectionTest.java[tags=collections-value-type-collection-order-column-remove-entity-example,indent=0]
include::{sourcedir}/BasicTypeOrderColumnElementCollectionTest.java[tags=collections-value-type-collection-order-column-remove-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/collections/ElementCollectionOrderColumnLifecycleRemove.sql[]
include::{sourcedir}/collections-value-type-collection-order-column-remove-example.sql[]
----
====
@ -116,16 +124,19 @@ Removing from the head or the middle of the collection requires deleting the ext
Embeddable type collections behave the same way as value type collections.
Adding embeddables to the collection triggers the associated insert statements and removing elements from the collection will generate delete statements.
[[collections-embeddable-type-collection-lifecycle-example]]
.Embeddable type collections
====
[source,java]
----
include::{sourcedir}/collections/EmbeddableElementCollectionLifecycle.java[]
include::{sourcedir}/EmbeddableTypeElementCollectionTest.java[tags=collections-embeddable-type-collection-lifecycle-entity-example,indent=0]
include::{sourcedir}/EmbeddableTypeElementCollectionTest.java[tags=collections-embeddable-type-collection-lifecycle-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/collections/EmbeddableElementCollectionLifecycle.sql[]
include::{sourcedir}/collections-embeddable-type-collection-lifecycle-example.sql[]
----
====
@ -166,16 +177,17 @@ Bags are unordered lists and we can have unidirectional bags or bidirectional on
The unidirectional bag is mapped using a single `@OneToMany` annotation on the parent side of the association.
Behind the scenes, Hibernate requires an association table to manage the parent-child relationship, as we can see in the following example:
[[collections-unidirectional-bag-example]]
.Unidirectional bag
====
[source,java]
----
include::{sourcedir}/collections/UnidirectionalBag.java[]
include::{sourcedir}/UnidirectionalBagTest.java[tags=collections-unidirectional-bag-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/collections/UnidirectionalBag.sql[]
include::{sourcedir}/collections-unidirectional-bag-example.sql[]
----
====
@ -187,16 +199,17 @@ Cascades can propagate an entity state transition from a parent entity to its ch
By marking the parent side with the `CascadeType.ALL` attribute, the unidirectional association lifecycle becomes very similar to that of a value type collection.
[[collections-unidirectional-bag-lifecycle-example]]
.Unidirectional bag lifecycle
====
[source,java]
----
include::{sourcedir}/collections/UnidirectionalBagLifecyclePersist.java[]
include::{sourcedir}/UnidirectionalBagTest.java[tags=collections-unidirectional-bag-lifecycle-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/collections/UnidirectionalBagLifecyclePersist.sql[]
include::{sourcedir}/collections-unidirectional-bag-lifecycle-example.sql[]
----
====
@ -214,37 +227,45 @@ Because the parent-side cannot uniquely identify each individual child, Hibernat
The bidirectional bag is the most common type of entity collection.
The `@ManyToOne` side is the owning side of the bidirectional bag association, while the `@OneToMany` is the _inverse_ side, being marked with the `mappedBy` attribute.
[[collections-bidirectional-bag-example]]
.Bidirectional bag
====
[source,java]
----
include::{sourcedir}/collections/BidirectionalBag.java[]
include::{sourcedir}/BidirectionalBagTest.java[tags=collections-bidirectional-bag-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/collections/BidirectionalBag.sql[]
include::{sourcedir}/collections-bidirectional-bag-example.sql[]
----
====
[[collections-bidirectional-bag-lifecycle-example]]
.Bidirectional bag lifecycle
====
[source,java]
----
include::{sourcedir}/collections/BidirectionalBagLifecycle.java[]
include::{sourcedir}/BidirectionalBagTest.java[tags=collections-bidirectional-bag-lifecycle-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/collections/BidirectionalBagLifecycle.sql[]
include::{sourcedir}/collections-bidirectional-bag-lifecycle-example.sql[]
----
====
[[collections-bidirectional-bag-orphan-removal-example]]
.Bidirectional bag with orphan removal
====
[source,java]
----
include::{sourcedir}/collections/BidirectionalBagOrphanRemoval.java[]
include::{sourcedir}/BidirectionalBagOrphanRemovalTest.java[tags=collections-bidirectional-bag-orphan-removal-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/collections-bidirectional-bag-orphan-removal-example.sql[]
----
====
@ -264,22 +285,24 @@ To preserve the collection element order, there are two possibilities:
When using the `@OrderBy` annotation, the mapping looks as follows:
[[collections-unidirectional-ordered-list-order-by-example]]
.Unidirectional `@OrderBy` list
====
[source,java]
----
include::{sourcedir}/collections/UnidirectionalOrderByList.java[]
include::{sourcedir}/UnidirectionalOrderedByListTest.java[tags=collections-unidirectional-ordered-list-order-by-example,indent=0]
----
====
The database mapping is the same as with the <<collections-unidirectional-bag>> example, so it won't be repeated.
Upon fetching the collection, Hibernate generates the following select statement:
[[collections-unidirectional-ordered-list-order-by-select-example]]
.Unidirectional `@OrderBy` list select statement
====
[source,sql]
----
include::{sourcedir}/collections/UnidirectionalOrderByListSelect.sql[]
include::{sourcedir}/collections-unidirectional-ordered-list-order-by-select-example.sql[]
----
====
@ -294,27 +317,29 @@ If no property is specified (e.g. `@OrderBy`), the primary key of the child enti
Another ordering option is to use the `@OrderColumn` annotation:
[[collections-unidirectional-ordered-list-order-column-example]]
.Unidirectional `@OrderColumn` list
====
[source,java]
----
include::{sourcedir}/collections/UnidirectionalOrderColumnList.java[]
include::{sourcedir}/UnidirectionalOrderColumnListTest.java[tags=collections-unidirectional-ordered-list-order-column-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/collections/UnidirectionalOrderColumnList.sql[]
include::{sourcedir}/collections-unidirectional-ordered-list-order-column-example.sql[]
----
====
This time, the link table takes the `order_id` column and uses it to materialize the collection element order.
When fetching the list, the following select query is executed:
[[collections-unidirectional-ordered-list-order-column-select-example]]
.Unidirectional `@OrderColumn` list select statement
====
[source,sql]
----
include::{sourcedir}/collections/UnidirectionalOrderColumnListSelect.sql[]
include::{sourcedir}/collections-unidirectional-ordered-list-order-column-select-example.sql[]
----
====
@ -325,11 +350,12 @@ With the `order_id` column in place, Hibernate can order the list in-memory afte
The mapping is similar with the <<collections-bidirectional-bag>> example, just that the parent side is going to be annotated with either `@OrderBy` or `@OrderColumn`.
[[collections-bidirectional-ordered-list-order-by-example]]
.Bidirectional `@OrderBy` list
====
[source,java]
----
include::{sourcedir}/collections/BidirectionalOrderByList.java[]
include::{sourcedir}/BidirectionalOrderByListTest.java[tags=collections-bidirectional-ordered-list-order-by-example,indent=0]
----
====
@ -337,16 +363,17 @@ Just like with the unidirectional `@OrderBy` list, the `number` column is used t
When using the `@OrderColumn` annotation, the `order_id` column is going to be embedded in the child table:
[[collections-bidirectional-ordered-list-order-column-example]]
.Bidirectional `@OrderColumn` list
====
[source,java]
----
include::{sourcedir}/collections/BidirectionalOrderColumnList.java[]
include::{sourcedir}/BidirectionalOrderColumnListTest.java[tags=collections-bidirectional-ordered-list-order-column-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/collections/BidirectionalOrderColumnList.sql[]
include::{sourcedir}/collections-bidirectional-ordered-list-order-column-example.sql[]
----
====
@ -362,11 +389,12 @@ Sets are collections that don't allow duplicate entries and Hibernate supports b
The unidirectional set uses a link table to hold the parent-child associations and the entity mapping looks as follows:
[[collections-unidirectional-set-example]]
.Unidirectional set
====
[source,java]
----
include::{sourcedir}/collections/UnidirectionalSet.java[]
include::{sourcedir}/UnidirectionalSetTest.java[tags=collections-unidirectional-set-example,indent=0]
----
====
@ -385,11 +413,12 @@ In the absence of a custom equals/hashCode implementation logic, Hibernate will
Just like bidirectional bags, the bidirectional set doesn't use a link table, and the child table has a foreign key referencing the parent table primary key.
The lifecycle is just like with bidirectional bags except for the duplicates which are filtered out.
[[collections-bidirectional-set-example]]
.Bidirectional set
====
[source,java]
----
include::{sourcedir}/collections/BidirectionalSet.java[]
include::{sourcedir}/BidirectionalSetTest.java[tags=collections-bidirectional-set-example,indent=0]
----
====
@ -404,11 +433,12 @@ According to the `SortedSet` contract, all elements must implement the comparabl
A `SortedSet` that relies on the natural sorting order given by the child element `Comparable` implementation logic must be annotated with the `@SortNatural` Hibernate annotation.
[[collections-unidirectional-sorted-set-natural-comparator-example]]
.Unidirectional natural sorted set
====
[source,java]
----
include::{sourcedir}/collections/UnidirectionalNaturalSortedSet.java[]
include::{sourcedir}/UnidirectionalSortedSetTest.java[tags=collections-unidirectional-sorted-set-natural-comparator-example,indent=0]
----
====
@ -416,11 +446,12 @@ The lifecycle and the database mapping are identical to the <<collections-unidir
To provide a custom sorting logic, Hibernate also provides a `@SortComparator` annotation:
[[collections-unidirectional-sorted-set-custom-comparator-example]]
.Unidirectional custom comparator sorted set
====
[source,java]
----
include::{sourcedir}/collections/UnidirectionalCustomSortedSet.java[]
include::{sourcedir}/UnidirectionalComparatorSortedSetTest.java[tags=collections-unidirectional-sorted-set-custom-comparator-example,indent=0]
----
====
@ -429,11 +460,14 @@ include::{sourcedir}/collections/UnidirectionalCustomSortedSet.java[]
The `@SortNatural` and `@SortComparator` work the same for bidirectional sorted sets too:
[[collections-bidirectional-sorted-set-example]]
.Bidirectional natural sorted set
====
[source,java]
----
include::{sourcedir}/collections/BidirectionalSortedSet.java[]
include::{sourcedir}/BidirectionalSortedSetTest.java[tags=collections-bidirectional-sorted-set-example,indent=0]
include::{sourcedir}/UnidirectionalComparatorSortedSetTest.java[lines=75..77,indent=0]
----
====
@ -455,31 +489,33 @@ Hibernate allows using the following map keys:
A map of value type must use the `@ElementCollection` annotation, just like value type lists, bags or sets.
[[collections-map-value-type-entity-key-example]]
.Value type map with an entity as a map key
====
[source,java]
----
include::{sourcedir}/collections/ElementCollectionMap.java[]
include::{sourcedir}/ElementCollectionMapTest.java[tags=collections-map-value-type-entity-key-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/collections/ElementCollectionMap.sql[]
include::{sourcedir}/collections-map-value-type-entity-key-example.sql[]
----
====
Adding entries to the map generates the following SQL statements:
[[collections-map-value-type-entity-key-add-example]]
.Adding value type map entries
====
[source,java]
----
include::{sourcedir}/collections/ElementCollectionMapPersist.java[]
include::{sourcedir}/ElementCollectionMapTest.java[tags=collections-map-value-type-entity-key-add-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/collections/ElementCollectionMapPersist.sql[]
include::{sourcedir}/collections-map-value-type-entity-key-add-example.sql[]
----
====
@ -490,16 +526,17 @@ A unidirectional map exposes a parent-child association from the parent-side onl
The following example shows a unidirectional map which also uses a `@MapKeyTemporal` annotation.
The map key is a timestamp and it's taken from the child entity table.
[[collections-map-unidirectional-example]]
.Unidirectional Map
====
[source,java]
----
include::{sourcedir}/collections/UnidirectionalMap.java[]
include::{sourcedir}/UnidirectionalMapTest.java[tags=collections-map-unidirectional-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/collections/UnidirectionalMap.sql[]
include::{sourcedir}/collections-map-unidirectional-example.sql[]
----
====
@ -509,16 +546,17 @@ include::{sourcedir}/collections/UnidirectionalMap.sql[]
Like most bidirectional associations, this relationship is owned by the child-side while the parent is the inverse side abd can propagate its own state transitions to the child entities.
In the following example, you can see that `@MapKeyEnumerated` was used so that the `Phone` enumeration becomes the map key.
[[collections-map-bidirectional-example]]
.Bidirectional Map
====
[source,java]
----
include::{sourcedir}/collections/BidirectionalMap.java[]
include::{sourcedir}/BidirectionalMapTest.java[tags=collections-map-bidirectional-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/collections/BidirectionalMap.sql[]
include::{sourcedir}/collections-map-bidirectional-example.sql[]
----
====
@ -534,16 +572,17 @@ Second, Java arrays are relevant for basic types only, since storing multiple em
By default, Hibernate will choose a BINARY type, as supported by the current `Dialect`.
[[collections-array-binary-example]]
.Binary arrays
====
[source,java]
----
include::{sourcedir}/collections/BaseTypeBinaryArray.java[]
include::{sourcedir}/ArrayTest.java[tags=collections-array-binary-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/collections/BaseTypeBinaryArray.sql[]
include::{sourcedir}/collections-array-binary-example.sql[]
----
====
@ -555,27 +594,33 @@ Collections not marked as such require a custom Hibernate `Type` and the collect
This is sometimes beneficial. Consider a use-case such as a `VARCHAR` column that represents a delimited list/set of Strings.
[[collections-comma-delimited-collection-example]]
.Comma delimited collection
====
[source,java]
----
include::{sourcedir}/collections/CommaDelimitedStringCollection.java[]
include::{sourcedir}/BasicTypeCollectionTest.java[tags=collections-comma-delimited-collection-example,indent=0]
include::{sourcedir}/type/CommaDelimitedStringsJavaTypeDescriptor.java[tags=collections-comma-delimited-collection-example,indent=0]
include::{sourcedir}/type/CommaDelimitedStringsType.java[tags=collections-comma-delimited-collection-example,indent=0]
----
====
The developer can use the comma-delimited collection like any other collection we've discussed so far and Hibernate will take care of the type transformation part.
The collection itself behaves like any other basic value type, as its lifecycle is bound to its owner entity.
[[collections-comma-delimited-collection-lifecycle-example]]
.Comma delimited collection lifecycle
====
[source,java]
----
include::{sourcedir}/collections/CommaDelimitedStringCollectionLifecycle.java[]
include::{sourcedir}/BasicTypeCollectionTest.java[tags=collections-comma-delimited-collection-lifecycle-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/collections/CommaDelimitedStringCollectionLifecycle.sql[]
include::{sourcedir}/collections-comma-delimited-collection-lifecycle-example.sql[]
----
====

View File

@ -1,106 +0,0 @@
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
@NaturalId
private String registrationNumber;
public Person() {}
public Person(String registrationNumber) {
this.registrationNumber = registrationNumber;
}
@ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE} )
private List<Address> addresses = new ArrayList<>();
public List<Address> getAddresses() {
return addresses;
}
public void addAddress(Address address) {
addresses.add(address);
address.getOwners().add(this);
}
public void removeAddress(Address address) {
addresses.remove(address);
address.getOwners().remove(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(registrationNumber, person.registrationNumber);
}
@Override
public int hashCode() {
return Objects.hash(registrationNumber);
}
}
@Entity
public class Address {
@Id
@GeneratedValue
private Long id;
private String street;
private String number;
private String postalCode;
@ManyToMany(mappedBy = "addresses")
private List<Person> owners = new ArrayList<>();
public Address() {}
public Address(String street, String number, String postalCode) {
this.street = street;
this.number = number;
this.postalCode = postalCode;
}
public Long getId() {
return id;
}
public String getStreet() {
return street;
}
public String getNumber() {
return number;
}
public String getPostalCode() {
return postalCode;
}
public List<Person> getOwners() {
return owners;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address address = (Address) o;
return Objects.equals(street, address.street) &&
Objects.equals(number, address.number) &&
Objects.equals(postalCode, address.postalCode);
}
@Override
public int hashCode() {
return Objects.hash(street, number, postalCode);
}
}

View File

@ -1,17 +0,0 @@
Person person1 = new Person("ABC-123");
Person person2 = new Person("DEF-456");
Address address1 = new Address("12th Avenue", "12A", "4005A");
Address address2 = new Address("18th Avenue", "18B", "4007B");
person1.addAddress(address1);
person1.addAddress(address2);
person2.addAddress(address1);
entityManager.persist(person1);
entityManager.persist(person2);
entityManager.flush();
person1.removeAddress(address1);

View File

@ -1,163 +0,0 @@
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
@NaturalId
private String registrationNumber;
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PersonAddress> addresses = new ArrayList<>();
public Person() {}
public Person(String registrationNumber) {
this.registrationNumber = registrationNumber;
}
public Long getId() {
return id;
}
public List<PersonAddress> getAddresses() {
return addresses;
}
public void addAddress(Address address) {
PersonAddress personAddress = new PersonAddress(this, address);
addresses.add(personAddress);
address.getOwners().add(personAddress);
}
public void removeAddress(Address address) {
PersonAddress personAddress = new PersonAddress(this, address);
address.getOwners().remove(personAddress);
addresses.remove(personAddress);
personAddress.setPerson(null);
personAddress.setAddress(null);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(registrationNumber, person.registrationNumber);
}
@Override
public int hashCode() {
return Objects.hash(registrationNumber);
}
}
@Entity
public class PersonAddress implements Serializable {
@Id
@ManyToOne
private Person person;
@Id
@ManyToOne
private Address address;
public PersonAddress() {}
public PersonAddress(Person person, Address address) {
this.person = person;
this.address = address;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PersonAddress that = (PersonAddress) o;
return Objects.equals(person, that.person) &&
Objects.equals(address, that.address);
}
@Override
public int hashCode() {
return Objects.hash(person, address);
}
}
@Entity
public class Address {
@Id
@GeneratedValue
private Long id;
private String street;
private String number;
private String postalCode;
@OneToMany(mappedBy = "address", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PersonAddress> owners = new ArrayList<>();
public Address() {}
public Address(String street, String number, String postalCode) {
this.street = street;
this.number = number;
this.postalCode = postalCode;
}
public Long getId() {
return id;
}
public String getStreet() {
return street;
}
public String getNumber() {
return number;
}
public String getPostalCode() {
return postalCode;
}
public List<PersonAddress> getOwners() {
return owners;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address address = (Address) o;
return Objects.equals(street, address.street) &&
Objects.equals(number, address.number) &&
Objects.equals(postalCode, address.postalCode);
}
@Override
public int hashCode() {
return Objects.hash(street, number, postalCode);
}
}

View File

@ -1,20 +0,0 @@
Person person1 = new Person("ABC-123");
Person person2 = new Person("DEF-456");
Address address1 = new Address("12th Avenue", "12A", "4005A");
Address address2 = new Address("18th Avenue", "18B", "4007B");
entityManager.persist(person1);
entityManager.persist(person2);
entityManager.persist(address1);
entityManager.persist(address2);
person1.addAddress(address1);
person1.addAddress(address2);
person2.addAddress(address1);
entityManager.flush();
person1.removeAddress(address1);

View File

@ -1,80 +0,0 @@
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
public Person() {}
public Person(Long id) {
this.id = id;
}
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Phone> phones = new ArrayList<>();
public List<Phone> getPhones() {
return phones;
}
public void addPhone(Phone phone) {
phones.add(phone);
phone.setPerson(this);
}
public void removePhone(Phone phone) {
phones.remove(phone);
phone.setPerson(null);
}
}
@Entity
public class Phone {
@Id
@GeneratedValue
private Long id;
@NaturalId
@Column(unique = true)
private String number;
@ManyToOne
private Person person;
public Phone() {}
public Phone(String number) {
this.number = number;
}
public Long getId() {
return id;
}
public String getNumber() {
return number;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Phone phone = (Phone) o;
return Objects.equals(number, phone.number);
}
@Override
public int hashCode() {
return Objects.hash(number);
}
}

View File

@ -1,10 +0,0 @@
Person person = new Person();
Phone phone1 = new Phone("123-456-7890");
Phone phone2 = new Phone("321-654-0987");
person.addPhone(phone1);
person.addPhone(phone2);
entityManager.persist(person);
entityManager.flush();
person.removePhone(phone1);

View File

@ -1,85 +0,0 @@
@Entity
public class Phone {
@Id
@GeneratedValue
private Long id;
private String number;
@OneToOne(mappedBy = "phone", cascade = CascadeType.ALL, orphanRemoval = true)
private PhoneDetails details;
public Phone() {}
public Phone(String number) {
this.number = number;
}
public Long getId() {
return id;
}
public String getNumber() {
return number;
}
public PhoneDetails getDetails() {
return details;
}
public void addDetails(PhoneDetails details) {
details.setPhone(this);
this.details = details;
}
public void removeDetails() {
if (details != null) {
details.setPhone(null);
this.details = null;
}
}
}
@Entity
public class PhoneDetails {
@Id
@GeneratedValue
private Long id;
private String provider;
private String technology;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "phone_id")
private Phone phone;
public PhoneDetails() {}
public PhoneDetails(String provider, String technology) {
this.provider = provider;
this.technology = technology;
}
public String getProvider() {
return provider;
}
public String getTechnology() {
return technology;
}
public void setTechnology(String technology) {
this.technology = technology;
}
public Phone getPhone() {
return phone;
}
public void setPhone(Phone phone) {
this.phone = phone;
}
}

View File

@ -1,14 +0,0 @@
Phone phone = new Phone("123-456-7890");
PhoneDetails details = new PhoneDetails("T-Mobile", "GSM");
phone.addDetails(details);
entityManager.persist(phone);
PhoneDetails otherDetails = new PhoneDetails("T-Mobile", "CDMA");
otherDetails.setPhone(phone);
entityManager.persist(otherDetails);
entityManager.flush();
entityManager.clear();
//throws javax.persistence.PersistenceException: org.hibernate.HibernateException: More than one row with the given identifier was found: 1
entityManager.find(Phone.class, phone.getId()).getDetails().getProvider();

View File

@ -1,5 +0,0 @@
Phone phone = new Phone("123-456-7890");
PhoneDetails details = new PhoneDetails("T-Mobile", "GSM");
phone.addDetails(details);
entityManager.persist(phone);

View File

@ -1,47 +0,0 @@
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
public Person() {}
}
@Entity
public class Phone {
@Id
@GeneratedValue
private Long id;
private String number;
@ManyToOne
@JoinColumn(name = "person_id",
foreignKey = @ForeignKey(name = "PERSON_ID_FK")
)
private Person person;
public Phone() {}
public Phone(String number) {
this.number = number;
}
public Long getId() {
return id;
}
public String getNumber() {
return number;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}

View File

@ -1,9 +0,0 @@
Person person = new Person();
entityManager.persist(person);
Phone phone = new Phone("123-456-7890");
phone.setPerson(person);
entityManager.persist(phone);
entityManager.flush();
phone.setPerson(null);

View File

@ -1,47 +0,0 @@
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
public Person() {}
@ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE} )
private List<Address> addresses = new ArrayList<>();
public List<Address> getAddresses() {
return addresses;
}
}
@Entity
public class Address {
@Id
@GeneratedValue
private Long id;
private String street;
private String number;
public Address() {}
public Address(String street, String number) {
this.street = street;
this.number = number;
}
public Long getId() {
return id;
}
public String getStreet() {
return street;
}
public String getNumber() {
return number;
}
}

View File

@ -1,17 +0,0 @@
Person person1 = new Person();
Person person2 = new Person();
Address address1 = new Address("12th Avenue", "12A");
Address address2 = new Address("18th Avenue", "18B");
person1.getAddresses().add(address1);
person1.getAddresses().add(address2);
person2.getAddresses().add(address1);
entityManager.persist(person1);
entityManager.persist(person2);
entityManager.flush();
person1.getAddresses().remove(address1);

View File

@ -1,2 +0,0 @@
Person person1 = entityManager.find(Person.class, personId);
entityManager.remove(person1);

View File

@ -1,40 +0,0 @@
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
public Person() {}
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private List<Phone> phones = new ArrayList<>();
public List<Phone> getPhones() {
return phones;
}
}
@Entity
public class Phone {
@Id
@GeneratedValue
private Long id;
private String number;
public Phone() {}
public Phone(String number) {
this.number = number;
}
public Long getId() {
return id;
}
public String getNumber() {
return number;
}
}

View File

@ -1,10 +0,0 @@
Person person = new Person();
Phone phone1 = new Phone("123-456-7890");
Phone phone2 = new Phone("321-654-0987");
person.getPhones().add(phone1);
person.getPhones().add(phone2);
entityManager.persist(person);
entityManager.flush();
person.getPhones().remove(phone1);

View File

@ -1,66 +0,0 @@
@Entity
public class Phone {
@Id
@GeneratedValue
private Long id;
private String number;
@OneToOne
@JoinColumn(name = "details_id")
private PhoneDetails details;
public Phone() {}
public Phone(String number) {
this.number = number;
}
public Long getId() {
return id;
}
public String getNumber() {
return number;
}
public PhoneDetails getDetails() {
return details;
}
public void setDetails(PhoneDetails details) {
this.details = details;
}
}
@Entity
public class PhoneDetails {
@Id
@GeneratedValue
private Long id;
private String provider;
private String technology;
public PhoneDetails() {}
public PhoneDetails(String provider, String technology) {
this.provider = provider;
this.technology = technology;
}
public String getProvider() {
return provider;
}
public String getTechnology() {
return technology;
}
public void setTechnology(String technology) {
this.technology = technology;
}
}

View File

@ -1,22 +0,0 @@
@Entity
public class Person {
@Id
private Long id;
public Person() {}
public Person(Long id) {
this.id = id;
}
private String[] phones;
public String[] getPhones() {
return phones;
}
public void setPhones(String[] phones) {
this.phones = phones;
}
}

View File

@ -1,90 +0,0 @@
@Entity
public class Person {
@Id
private Long id;
public Person() {}
public Person(Long id) {
this.id = id;
}
@OneToMany(
mappedBy = "person",
cascade = CascadeType.ALL
)
private List<Phone> phones = new ArrayList<>();
public List<Phone> getPhones() {
return phones;
}
public void addPhone(Phone phone) {
phones.add(phone);
phone.setPerson(this);
}
public void removePhone(Phone phone) {
phones.remove(phone);
phone.setPerson(null);
}
}
@Entity
public class Phone {
@Id
private Long id;
private String type;
@Column(unique = true)
@NaturalId
private String number;
@ManyToOne
private Person person;
public Phone() {
}
public Phone(Long id, String type, String number) {
this.id = id;
this.type = type;
this.number = number;
}
public Long getId() {
return id;
}
public String getType() {
return type;
}
public String getNumber() {
return number;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Phone phone = (Phone) o;
return Objects.equals(number, phone.number);
}
@Override
public int hashCode() {
return Objects.hash(number);
}
}

View File

@ -1,4 +0,0 @@
person.addPhone(new Phone(1L, "landline", "028-234-9876"));
person.addPhone(new Phone(2L, "mobile", "072-122-9876"));
entityManager.flush();
person.removePhone(person.getPhones().get(0));

View File

@ -1,6 +0,0 @@
@OneToMany(
mappedBy = "person",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<Phone> phones = new ArrayList<>();

View File

@ -1,71 +0,0 @@
@Entity
public class Person {
@Id
private Long id;
public Person() {}
public Person(Long id) {
this.id = id;
}
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true)
@MapKey(name="type")
@MapKeyEnumerated
private Map<PhoneType, Phone> phoneRegister = new HashMap<>();
public Map<PhoneType, Phone> getPhoneRegister() {
return phoneRegister;
}
public void addPhone(Phone phone) {
phone.setPerson(this);
phoneRegister.put(phone.getType(), phone);
}
}
@Entity
public class Phone {
@Id
@GeneratedValue
private Long id;
private PhoneType type;
private String number;
private Date since;
@ManyToOne
private Person person;
public Phone() {}
public Phone(PhoneType type, String number, Date since) {
this.type = type;
this.number = number;
this.since = since;
}
public PhoneType getType() {
return type;
}
public String getNumber() {
return number;
}
public Date getSince() {
return since;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}

View File

@ -1,3 +0,0 @@
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
@OrderBy("number")
private List<Phone> phones = new ArrayList<>();

View File

@ -1,3 +0,0 @@
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
@OrderColumn(name = "order_id")
private List<Phone> phones = new ArrayList<>();

View File

@ -1,86 +0,0 @@
@Entity
public class Person {
@Id
private Long id;
public Person() {}
public Person(Long id) {
this.id = id;
}
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
private Set<Phone> phones = new HashSet<>();
public Set<Phone> getPhones() {
return phones;
}
public void addPhone(Phone phone) {
phones.add(phone);
phone.setPerson(this);
}
public void removePhone(Phone phone) {
phones.remove(phone);
phone.setPerson(null);
}
}
@Entity
public class Phone {
@Id
private Long id;
private String type;
@Column(unique = true)
@NaturalId
private String number;
@ManyToOne
private Person person;
public Phone() {}
public Phone(Long id, String type, String number) {
this.id = id;
this.type = type;
this.number = number;
}
public Long getId() {
return id;
}
public String getType() {
return type;
}
public String getNumber() {
return number;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Phone phone = (Phone) o;
return Objects.equals(number, phone.number);
}
@Override
public int hashCode() {
return Objects.hash(number);
}
}

View File

@ -1,7 +0,0 @@
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
@SortNatural
private SortedSet<Phone> phones = new TreeSet<>();
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
@SortComparator(ReverseComparator.class)
private SortedSet<Phone> phones = new TreeSet<>();

View File

@ -1,17 +0,0 @@
@Entity
public class Person {
@Id
private Long id;
@ElementCollection
private List<String> phones = new ArrayList<>();
public List<String> getPhones() {
return phones;
}
}
Person person = entityManager.find( Person.class, 1L );
//Throws java.lang.ClassCastException: org.hibernate.collection.internal.PersistentBag cannot be cast to java.util.ArrayList
ArrayList<String> phones = ( ArrayList<String> ) person.getPhones();

View File

@ -1,67 +0,0 @@
@Entity
public class Person {
@Id
private Long id;
@Type(type = "comma_delimited_strings")
private List<String> phones = new ArrayList<>();
public List<String> getPhones() {
return phones;
}
}
public class CommaDelimitedStringsJavaTypeDescriptor extends AbstractTypeDescriptor<List> {
public static final String DELIMITER = ",";
public CommaDelimitedStringsJavaTypeDescriptor() {
super(
List.class,
new MutableMutabilityPlan<List>() {
@Override
protected List deepCopyNotNull(List value) {
return new ArrayList(value);
}
}
);
}
@Override
public String toString(List value) {
return ((List<String>) value).stream().collect(Collectors.joining(DELIMITER));
}
@Override
public List fromString(String string) {
List<String> values = new ArrayList<>();
Collections.addAll(values, string.split(DELIMITER));
return values;
}
@Override
public <X> X unwrap(List value, Class<X> type, WrapperOptions options) {
return (X) toString(value);
}
@Override
public <X> List wrap(X value, WrapperOptions options) {
return fromString((String) value);
}
}
public class CommaDelimitedStringsType extends AbstractSingleColumnStandardBasicType<List> {
public CommaDelimitedStringsType() {
super(
VarcharTypeDescriptor.INSTANCE,
new CommaDelimitedStringsJavaTypeDescriptor()
);
}
@Override
public String getName() {
return "comma_delimited_strings";
}
}

View File

@ -1,6 +0,0 @@
person.phones.add("027-123-4567");
person.phones.add("028-234-9876");
session.flush();
person.getPhones().remove(0);
session.flush();

View File

@ -1,3 +0,0 @@
person.getPhones().clear();
person.getPhones().add( "123-456-7890" );
person.getPhones().add( "456-000-1234" );

View File

@ -1,53 +0,0 @@
@Entity
public class Person {
@Id
private Long id;
public Person() {
}
public Person(Long id) {
this.id = id;
}
@Temporal(TemporalType.TIMESTAMP)
@ElementCollection
@CollectionTable(name="phone_register")
@Column(name="since")
@MapKeyJoinColumn(name = "phone_id", referencedColumnName="id")
private Map<Phone, Date> phoneRegister = new HashMap<>();
public Map<Phone, Date> getPhoneRegister() {
return phoneRegister;
}
}
public enum PhoneType {
LAND_LINE,
MOBILE
}
@Embeddable
public class Phone {
private PhoneType type;
private String number;
public Phone() {
}
public Phone(PhoneType type, String number) {
this.type = type;
this.number = number;
}
public PhoneType getType() {
return type;
}
public String getNumber() {
return number;
}
}

View File

@ -1,7 +0,0 @@
person.getPhoneRegister().put(
new Phone( PhoneType.LAND_LINE, "028-234-9876" ), new Date()
);
person.getPhoneRegister().put(
new Phone( PhoneType.MOBILE, "072-122-9876" ), new Date()
);

View File

@ -1,5 +0,0 @@
@ElementCollection
@OrderColumn(name = "order_id")
private List<String> phones = new ArrayList<>();
person.getPhones().remove( 0 );

View File

@ -1,39 +0,0 @@
@Entity
public class Person {
@Id
private Long id;
@ElementCollection
private List<Phone> phones = new ArrayList<>();
public List<Phone> getPhones() {
return phones;
}
}
@Embeddable
public class Phone {
private String type;
private String number;
public Phone() {}
public Phone(String type, String number) {
this.type = type;
this.number = number;
}
public String getType() {
return type;
}
public String getNumber() {
return number;
}
}
person.getPhones().add( new Phone( "landline", "028-234-9876" ) );
person.getPhones().add( new Phone( "mobile", "072-122-9876" ) );

View File

@ -1,51 +0,0 @@
@Entity
public class Person {
@Id
private Long id;
public Person() {}
public Person( Long id ) {
this.id = id;
}
@OneToMany(cascade = CascadeType.ALL)
private List<Phone> phones = new ArrayList<>();
public List<Phone> getPhones() {
return phones;
}
}
@Entity
public class Phone {
@Id
private Long id;
private String type;
private String number;
public Phone() {
}
public Phone( Long id, String type, String number ) {
this.id = id;
this.type = type;
this.number = number;
}
public Long getId() {
return id;
}
public String getType() {
return type;
}
public String getNumber() {
return number;
}
}

View File

@ -1,4 +0,0 @@
Person person = new Person( 1L );
person.getPhones().add( new Phone( 1L, "landline", "028-234-9876" ) );
person.getPhones().add( new Phone( 2L, "mobile", "072-122-9876" ) );
entityManager.persist( person );

View File

@ -1,79 +0,0 @@
@Entity
public class Person {
@Id
private Long id;
public Person() {
}
public Person(Long id) {
this.id = id;
}
@OneToMany(cascade = CascadeType.ALL)
@SortComparator(ReverseComparator.class)
private SortedSet<Phone> phones = new TreeSet<>();
public Set<Phone> getPhones() {
return phones;
}
}
public class ReverseComparator implements Comparator<Phone> {
@Override
public int compare(Phone o1, Phone o2) {
return o2.compareTo(o1);
}
}
@Entity
public class Phone implements Comparable<Phone> {
@Id
private Long id;
private String type;
@NaturalId
private String number;
public Phone() {
}
public Phone(Long id, String type, String number) {
this.id = id;
this.type = type;
this.number = number;
}
public Long getId() {
return id;
}
public String getType() {
return type;
}
public String getNumber() {
return number;
}
@Override
public int compareTo(Phone o) {
return number.compareTo(o.getNumber());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Phone phone = (Phone) o;
return Objects.equals(number, phone.number);
}
@Override
public int hashCode() {
return Objects.hash(number);
}
}

View File

@ -1,64 +0,0 @@
@Entity
public class Person {
@Id
private Long id;
public Person() {
}
public Person(Long id) {
this.id = id;
}
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinTable(
name="phone_register",
joinColumns = @JoinColumn(name = "phone_id"),
inverseJoinColumns = @JoinColumn(name = "person_id"))
@MapKey(name="since")
@MapKeyTemporal(TemporalType.TIMESTAMP)
private Map<Date, Phone> phoneRegister = new HashMap<>();
public Map<Date, Phone> getPhoneRegister() {
return phoneRegister;
}
public void addPhone(Phone phone) {
phoneRegister.put(phone.getSince(), phone);
}
}
@Entity
public class Phone {
@Id
@GeneratedValue
private Long id;
private PhoneType type;
private String number;
private Date since;
public Phone() {}
public Phone(PhoneType type, String number, Date since) {
this.type = type;
this.number = number;
this.since = since;
}
public PhoneType getType() {
return type;
}
public String getNumber() {
return number;
}
public Date getSince() {
return since;
}
}

View File

@ -1,72 +0,0 @@
@Entity
public class Person {
@Id
private Long id;
public Person() {
}
public Person(Long id) {
this.id = id;
}
@OneToMany(cascade = CascadeType.ALL)
@SortNatural
private SortedSet<Phone> phones = new TreeSet<>();
public Set<Phone> getPhones() {
return phones;
}
}
@Entity
public class Phone implements Comparable<Phone> {
@Id
private Long id;
private String type;
@NaturalId
private String number;
public Phone() {
}
public Phone(Long id, String type, String number) {
this.id = id;
this.type = type;
this.number = number;
}
public Long getId() {
return id;
}
public String getType() {
return type;
}
public String getNumber() {
return number;
}
@Override
public int compareTo(Phone o) {
return number.compareTo(o.getNumber());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Phone phone = (Phone) o;
return Objects.equals(number, phone.number);
}
@Override
public int hashCode() {
return Objects.hash(number);
}
}

View File

@ -1,52 +0,0 @@
@Entity
public class Person {
@Id
private Long id;
public Person() {}
public Person(Long id) {
this.id = id;
}
@OneToMany(cascade = CascadeType.ALL)
@OrderBy("number")
private List<Phone> phones = new ArrayList<>();
public List<Phone> getPhones() {
return phones;
}
}
@Entity
public class Phone {
@Id
private Long id;
private String type;
private String number;
public Phone() {
}
public Phone(Long id, String type, String number) {
this.id = id;
this.type = type;
this.number = number;
}
public Long getId() {
return id;
}
public String getType() {
return type;
}
public String getNumber() {
return number;
}
}

View File

@ -1,3 +0,0 @@
@OneToMany(cascade = CascadeType.ALL)
@OrderColumn(name = "order_id")
private List<Phone> phones = new ArrayList<>();

View File

@ -1,66 +0,0 @@
@Entity
public class Person {
@Id
private Long id;
public Person() {
}
public Person(Long id) {
this.id = id;
}
@OneToMany(cascade = CascadeType.ALL)
private Set<Phone> phones = new HashSet<>();
public Set<Phone> getPhones() {
return phones;
}
}
@Entity
public class Phone {
@Id
private Long id;
private String type;
@NaturalId
private String number;
public Phone() {
}
public Phone(Long id, String type, String number) {
this.id = id;
this.type = type;
this.number = number;
}
public Long getId() {
return id;
}
public String getType() {
return type;
}
public String getNumber() {
return number;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Phone phone = (Phone) o;
return Objects.equals(number, phone.number);
}
@Override
public int hashCode() {
return Objects.hash(number);
}
}

View File

@ -1,73 +0,0 @@
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Account {
@Id
private Long id;
private String owner;
private BigDecimal balance;
private BigDecimal interestRate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public BigDecimal getBalance() {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
public BigDecimal getInterestRate() {
return interestRate;
}
public void setInterestRate(BigDecimal interestRate) {
this.interestRate = interestRate;
}
}
@Entity
public class DebitAccount extends Account {
private BigDecimal overdraftFee;
public BigDecimal getOverdraftFee() {
return overdraftFee;
}
public void setOverdraftFee(BigDecimal overdraftFee) {
this.overdraftFee = overdraftFee;
}
}
@Entity
public class CreditAccount extends Account {
private BigDecimal creditLimit;
public BigDecimal getCreditLimit() {
return creditLimit;
}
public void setCreditLimit(BigDecimal creditLimit) {
this.creditLimit = creditLimit;
}
}

View File

@ -1,29 +0,0 @@
@Entity(name = "DebitAccount")
@PrimaryKeyJoinColumn(name = "account_id")
public static class DebitAccount extends Account {
private BigDecimal overdraftFee;
public BigDecimal getOverdraftFee() {
return overdraftFee;
}
public void setOverdraftFee(BigDecimal overdraftFee) {
this.overdraftFee = overdraftFee;
}
}
@Entity(name = "CreditAccount")
@PrimaryKeyJoinColumn(name = "account_id")
public static class CreditAccount extends Account {
private BigDecimal creditLimit;
public BigDecimal getCreditLimit() {
return creditLimit;
}
public void setCreditLimit(BigDecimal creditLimit) {
this.creditLimit = creditLimit;
}
}

View File

@ -1,3 +0,0 @@
List<Account> accounts = entityManager
.createQuery( "select a from Account a" )
.getResultList();

View File

@ -1,72 +0,0 @@
@MappedSuperclass
public static class Account {
@Id
private Long id;
private String owner;
private BigDecimal balance;
private BigDecimal interestRate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public BigDecimal getBalance() {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
public BigDecimal getInterestRate() {
return interestRate;
}
public void setInterestRate(BigDecimal interestRate) {
this.interestRate = interestRate;
}
}
@Entity
public class DebitAccount extends Account {
private BigDecimal overdraftFee;
public BigDecimal getOverdraftFee() {
return overdraftFee;
}
public void setOverdraftFee(BigDecimal overdraftFee) {
this.overdraftFee = overdraftFee;
}
}
@Entity
public class CreditAccount extends Account {
private BigDecimal creditLimit;
public BigDecimal getCreditLimit() {
return creditLimit;
}
public void setCreditLimit(BigDecimal creditLimit) {
this.creditLimit = creditLimit;
}
}

View File

@ -1,73 +0,0 @@
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Account {
@Id
private Long id;
private String owner;
private BigDecimal balance;
private BigDecimal interestRate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public BigDecimal getBalance() {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
public BigDecimal getInterestRate() {
return interestRate;
}
public void setInterestRate(BigDecimal interestRate) {
this.interestRate = interestRate;
}
}
@Entity
public class DebitAccount extends Account {
private BigDecimal overdraftFee;
public BigDecimal getOverdraftFee() {
return overdraftFee;
}
public void setOverdraftFee(BigDecimal overdraftFee) {
this.overdraftFee = overdraftFee;
}
}
@Entity
public class CreditAccount extends Account {
private BigDecimal creditLimit;
public BigDecimal getCreditLimit() {
return creditLimit;
}
public void setCreditLimit(BigDecimal creditLimit) {
this.creditLimit = creditLimit;
}
}

View File

@ -1,109 +0,0 @@
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorFormula(
"case when debitKey is not null " +
"then 'Debit' " +
"else ( " +
" case when creditKey is not null " +
" then 'Credit' " +
" else 'Unknown' " +
" end ) " +
"end "
)
public class Account {
@Id
private Long id;
private String owner;
private BigDecimal balance;
private BigDecimal interestRate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public BigDecimal getBalance() {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
public BigDecimal getInterestRate() {
return interestRate;
}
public void setInterestRate(BigDecimal interestRate) {
this.interestRate = interestRate;
}
}
@Entity
@DiscriminatorValue(value = "Debit")
public class DebitAccount extends Account {
private String debitKey;
private BigDecimal overdraftFee;
private DebitAccount() {}
public DebitAccount(String debitKey) {
this.debitKey = debitKey;
}
public String getDebitKey() {
return debitKey;
}
public BigDecimal getOverdraftFee() {
return overdraftFee;
}
public void setOverdraftFee(BigDecimal overdraftFee) {
this.overdraftFee = overdraftFee;
}
}
@Entity
@DiscriminatorValue(value = "Credit")
public static class CreditAccount extends Account {
private String creditKey;
private BigDecimal creditLimit;
private CreditAccount() {}
public CreditAccount(String creditKey) {
this.creditKey = creditKey;
}
public String getCreditKey() {
return creditKey;
}
public BigDecimal getCreditLimit() {
return creditLimit;
}
public void setCreditLimit(BigDecimal creditLimit) {
this.creditLimit = creditLimit;
}
}

View File

@ -1,16 +0,0 @@
DebitAccount debitAccount = new DebitAccount();
debitAccount.setId(1L);
debitAccount.setOwner("John Doe");
debitAccount.setBalance(BigDecimal.valueOf(100));
debitAccount.setInterestRate(BigDecimal.valueOf(1.5d));
debitAccount.setOverdraftFee(BigDecimal.valueOf(25));
CreditAccount creditAccount = new CreditAccount();
creditAccount.setId(2L);
creditAccount.setOwner("John Doe");
creditAccount.setBalance(BigDecimal.valueOf(1000));
creditAccount.setInterestRate(BigDecimal.valueOf(1.9d));
creditAccount.setCreditLimit(BigDecimal.valueOf(5000));
entityManager.persist(debitAccount);
entityManager.persist(creditAccount);

View File

@ -1,3 +0,0 @@
List<Account> accounts = entityManager
.createQuery( "select a from Account a" )
.getResultList();

View File

@ -1,73 +0,0 @@
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Account {
@Id
private Long id;
private String owner;
private BigDecimal balance;
private BigDecimal interestRate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public BigDecimal getBalance() {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
public BigDecimal getInterestRate() {
return interestRate;
}
public void setInterestRate(BigDecimal interestRate) {
this.interestRate = interestRate;
}
}
@Entity
public class DebitAccount extends Account {
private BigDecimal overdraftFee;
public BigDecimal getOverdraftFee() {
return overdraftFee;
}
public void setOverdraftFee(BigDecimal overdraftFee) {
this.overdraftFee = overdraftFee;
}
}
@Entity
public class CreditAccount extends Account {
private BigDecimal creditLimit;
public BigDecimal getCreditLimit() {
return creditLimit;
}
public void setCreditLimit(BigDecimal creditLimit) {
this.creditLimit = creditLimit;
}
}

View File

@ -1,3 +0,0 @@
List<Account> accounts = entityManager
.createQuery( "select a from Account a" )
.getResultList();

View File

@ -1,6 +1,6 @@
[[entity-inheritance]]
=== Inheritance
:sourcedir: extras
:sourcedir: ../../../../../test/java/org/hibernate/jpa/test/userguide/inheritance
Although relational database systems don't provide support for inheritance, Hibernate provides several strategies to leverage this object-oriented trait onto domain model entities:
@ -18,16 +18,17 @@ image:images/domain/inheritance/inheritance_class_diagram.svg[Inheritance class
When using `MappedSuperclass`, the inheritance is visible in the domain model only and ach database table contains both the base class and the subclass properties.
[[entity-inheritance-mapped-superclass-example]]
.`@MappedSuperclass` inheritance
====
[source,java]
----
include::{sourcedir}/inheritance/MappedSuperclass.java[]
include::{sourcedir}/MappedSuperclassTest.java[tags=entity-inheritance-mapped-superclass-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/inheritance/MappedSuperclass.sql[]
include::{sourcedir}/entity-inheritance-mapped-superclass-example.sql[]
----
====
@ -49,32 +50,34 @@ Version and id properties are assumed to be inherited from the root class.
When omitting an explicit inheritance strategy (e.g. `@Inheritance`), JPA will choose the `SINGLE_TABLE` strategy by default.
====
[[entity-inheritance-single-table-example]]
.Single Table inheritance
====
[source,java]
----
include::{sourcedir}/inheritance/SingleTable.java[]
include::{sourcedir}/SingleTableTest.java[tags=entity-inheritance-single-table-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/inheritance/SingleTable.sql[]
include::{sourcedir}/entity-inheritance-single-table-example.sql[]
----
====
Each subclass in a hierarchy must define a unique discriminator value, which is used to differentiate between rows belonging to separate subclass types.
If this is not specified, the `DTYPE` column is used as a discriminator, storing the associated subclass name.
[[entity-inheritance-single-table-persist-example]]
.Single Table inheritance discriminator column
====
[source,java]
----
include::{sourcedir}/inheritance/SingleTablePersist.java[]
include::{sourcedir}/SingleTableTest.java[tags=entity-inheritance-single-table-persist-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/inheritance/SingleTablePersist.sql[]
include::{sourcedir}/entity-inheritance-single-table-persist-example.sql[]
----
====
@ -111,16 +114,17 @@ There used to be `@org.hibernate.annotations.ForceDiscriminator` annotation whic
Assuming a legacy database schema where the discriminator is based on inspecting a certain column,
we can take advantage of the Hibernate specific `@DiscriminatorFormula` annotation and map the inheritance model as follows:
[[entity-inheritance-single-table-discriminator-formula-example]]
.Single Table discriminator formula
====
[source,java]
----
include::{sourcedir}/inheritance/SingleTableDiscriminatorFormula.java[]
include::{sourcedir}/SingleTableDiscriminatorFormulaTest.java[tags=entity-inheritance-single-table-discriminator-formula-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/inheritance/SingleTableDiscriminatorFormula.sql[]
include::{sourcedir}/entity-inheritance-single-table-discriminator-formula-example.sql[]
----
====
@ -135,16 +139,17 @@ Because all subclass columns are stored in a single table, it's not possible to
When using polymorphic queries, only a single table is required to be scanned to fetch all associated subclass instances.
[[entity-inheritance-single-table-query-example]]
.Single Table polymorphic query
====
[source,java]
----
include::{sourcedir}/inheritance/SingleTableQuery.java[]
include::{sourcedir}/SingleTableTest.java[tags=entity-inheritance-single-table-query-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/inheritance/SingleTableQuery.sql[]
include::{sourcedir}/entity-inheritance-single-table-query-example.sql[]
----
====
@ -158,16 +163,17 @@ An inherited state is retrieved by joining with the table of the superclass.
A discriminator column is not required for this mapping strategy.
Each subclass must, however, declare a table column holding the object identifier.
[[entity-inheritance-joined-table-example]]
.Join Table
====
[source,java]
----
include::{sourcedir}/inheritance/JoinTable.java[]
include::{sourcedir}/JoinTableTest.java[tags=entity-inheritance-joined-table-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/inheritance/JoinTable.sql[]
include::{sourcedir}/entity-inheritance-joined-table-example.sql[]
----
====
@ -179,31 +185,33 @@ The table name still defaults to the non qualified class name.
Also if `@PrimaryKeyJoinColumn` is not set, the primary key / foreign key columns are assumed to have the same names as the primary key columns of the primary table of the superclass.
====
[[entity-inheritance-joined-table-primary-key-join-column-example]]
.Join Table with `@PrimaryKeyJoinColumn`
====
[source,java]
----
include::{sourcedir}/inheritance/JoinTablePrimaryKeyJoinColumn.java[]
include::{sourcedir}/JoinTablePrimaryKeyJoinColumnTest.java[tags=entity-inheritance-joined-table-primary-key-join-column-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/inheritance/JoinTablePrimaryKeyJoinColumn.sql[]
include::{sourcedir}/entity-inheritance-joined-table-primary-key-join-column-example.sql[]
----
====
When using polymorphic queries, the base class table must be joined with all subclass tables to fetch every associated subclass instance.
[[entity-inheritance-joined-table-query-example]]
.Join Table polymorphic query
====
[source,java]
----
include::{sourcedir}/inheritance/JoinTableQuery.java[]
include::{sourcedir}/JoinTableTest.java[tags=entity-inheritance-joined-table-query-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/inheritance/JoinTableQuery.sql[]
include::{sourcedir}/entity-inheritance-joined-table-query-example.sql[]
----
====
@ -223,16 +231,17 @@ In Hibernate, it is not necessary to explicitly map such inheritance hierarchies
You can map each class as a separate entity root.
However, if you wish use polymorphic associations (e.g. an association to the superclass of your hierarchy), you need to use the union subclass mapping.
[[entity-inheritance-table-per-class-example]]
.Table per class
====
[source,java]
----
include::{sourcedir}/inheritance/TablePerClass.java[]
include::{sourcedir}/TablePerClassTest.java[tags=entity-inheritance-table-per-class-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/inheritance/TablePerClass.sql[]
include::{sourcedir}/entity-inheritance-table-per-class-example.sql[]
----
====
@ -242,12 +251,12 @@ When using polymorphic queries, a UNION is required to fetch the the base class
====
[source,java]
----
include::{sourcedir}/inheritance/TablePerClassQuery.java[]
include::{sourcedir}/TablePerClassTest.java[tags=entity-inheritance-table-per-class-query-example,indent=0]
----
[source,sql]
----
include::{sourcedir}/inheritance/TablePerClassQuery.sql[]
include::{sourcedir}/entity-inheritance-table-per-class-query-example.sql[]
----
====

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.association;
package org.hibernate.jpa.test.userguide.associations;
import java.io.Serializable;
import java.util.ArrayList;

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.association;
package org.hibernate.jpa.test.userguide.associations;
import java.io.Serializable;
import java.util.Objects;

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.association;
package org.hibernate.jpa.test.userguide.associations;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.association;
package org.hibernate.jpa.test.userguide.associations;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.association;
package org.hibernate.jpa.test.userguide.associations;
import java.util.ArrayList;
import java.util.List;
@ -25,7 +25,7 @@ import static org.hibernate.jpa.test.util.TransactionUtil.doInJPA;
/**
* @author Vlad Mihalcea
*/
public class BidirectionalManyToManyTest extends BaseEntityManagerFunctionalTestCase {
public class ManyToManyBidirectionalTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
@ -38,6 +38,7 @@ public class BidirectionalManyToManyTest extends BaseEntityManagerFunctionalTest
@Test
public void testLifecycle() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::associations-many-to-many-bidirectional-lifecycle-example[]
Person person1 = new Person( "ABC-123" );
Person person2 = new Person( "DEF-456" );
@ -55,9 +56,11 @@ public class BidirectionalManyToManyTest extends BaseEntityManagerFunctionalTest
entityManager.flush();
person1.removeAddress( address1 );
//end::associations-many-to-many-bidirectional-lifecycle-example[]
} );
}
//tag::associations-many-to-many-bidirectional-example[]
@Entity(name = "Person")
public static class Person {
@ -173,4 +176,5 @@ public class BidirectionalManyToManyTest extends BaseEntityManagerFunctionalTest
return Objects.hash( street, number, postalCode );
}
}
//end::associations-many-to-many-bidirectional-example[]
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.association;
package org.hibernate.jpa.test.userguide.associations;
import java.io.Serializable;
import java.util.ArrayList;
@ -29,9 +29,9 @@ import static org.hibernate.jpa.test.util.TransactionUtil.doInJPA;
/**
* @author Vlad Mihalcea
*/
public class BidirectionalTwoOneToManyTest extends BaseEntityManagerFunctionalTestCase {
public class ManyToManyBidirectionalWithLinkEntityTest extends BaseEntityManagerFunctionalTestCase {
private static final Logger log = Logger.getLogger( BidirectionalTwoOneToManyTest.class );
private static final Logger log = Logger.getLogger( ManyToManyBidirectionalWithLinkEntityTest.class );
@Override
protected Class<?>[] getAnnotatedClasses() {
@ -45,6 +45,7 @@ public class BidirectionalTwoOneToManyTest extends BaseEntityManagerFunctionalTe
@Test
public void testLifecycle() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::associations-many-to-many-bidirectional-with-link-entity-lifecycle-example[]
Person person1 = new Person( "ABC-123" );
Person person2 = new Person( "DEF-456" );
@ -66,9 +67,11 @@ public class BidirectionalTwoOneToManyTest extends BaseEntityManagerFunctionalTe
log.info( "Removing address" );
person1.removeAddress( address1 );
//end::associations-many-to-many-bidirectional-with-link-entity-lifecycle-example[]
} );
}
//tag::associations-many-to-many-bidirectional-with-link-entity-example[]
@Entity(name = "Person")
public static class Person implements Serializable {
@ -247,4 +250,5 @@ public class BidirectionalTwoOneToManyTest extends BaseEntityManagerFunctionalTe
return Objects.hash( street, number, postalCode );
}
}
//end::associations-many-to-many-bidirectional-with-link-entity-example[]
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.association;
package org.hibernate.jpa.test.userguide.associations;
import java.util.ArrayList;
import java.util.List;
@ -25,9 +25,9 @@ import static org.hibernate.jpa.test.util.TransactionUtil.doInJPA;
/**
* @author Vlad Mihalcea
*/
public class UnidirectionalManyToManyTest extends BaseEntityManagerFunctionalTestCase {
public class ManyToManyUnidirectionalTest extends BaseEntityManagerFunctionalTestCase {
private static final Logger log = Logger.getLogger( UnidirectionalManyToManyTest.class );
private static final Logger log = Logger.getLogger( ManyToManyUnidirectionalTest.class );
@Override
protected Class<?>[] getAnnotatedClasses() {
@ -40,6 +40,7 @@ public class UnidirectionalManyToManyTest extends BaseEntityManagerFunctionalTes
@Test
public void testLifecycle() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::associations-many-to-many-unidirectional-lifecycle-example[]
Person person1 = new Person();
Person person2 = new Person();
@ -57,6 +58,7 @@ public class UnidirectionalManyToManyTest extends BaseEntityManagerFunctionalTes
entityManager.flush();
person1.getAddresses().remove( address1 );
//end::associations-many-to-many-unidirectional-lifecycle-example[]
} );
}
@ -81,11 +83,14 @@ public class UnidirectionalManyToManyTest extends BaseEntityManagerFunctionalTes
} );
doInJPA( this::entityManagerFactory, entityManager -> {
log.info( "Remove" );
//tag::associations-many-to-many-unidirectional-remove-example[]
Person person1 = entityManager.find( Person.class, personId );
entityManager.remove( person1 );
//end::associations-many-to-many-unidirectional-remove-example[]
} );
}
//tag::associations-many-to-many-unidirectional-example[]
@Entity(name = "Person")
public static class Person {
@ -134,4 +139,5 @@ public class UnidirectionalManyToManyTest extends BaseEntityManagerFunctionalTes
return number;
}
}
//end::associations-many-to-many-unidirectional-example[]
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.association;
package org.hibernate.jpa.test.userguide.associations;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
@ -35,6 +35,7 @@ public class ManyToOneTest extends BaseEntityManagerFunctionalTestCase {
@Test
public void testLifecycle() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::associations-many-to-one-lifecycle-example[]
Person person = new Person();
entityManager.persist( person );
@ -44,9 +45,11 @@ public class ManyToOneTest extends BaseEntityManagerFunctionalTestCase {
entityManager.flush();
phone.setPerson( null );
//end::associations-many-to-one-lifecycle-example[]
} );
}
//tag::associations-many-to-one-example[]
@Entity(name = "Person")
public static class Person {
@ -96,4 +99,5 @@ public class ManyToOneTest extends BaseEntityManagerFunctionalTestCase {
this.person = person;
}
}
//end::associations-many-to-one-example[]
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.association;
package org.hibernate.jpa.test.userguide.associations;
import java.util.ArrayList;
import java.util.List;
@ -27,7 +27,7 @@ import static org.hibernate.jpa.test.util.TransactionUtil.doInJPA;
/**
* @author Vlad Mihalcea
*/
public class BidirectionalOneToManyTest extends BaseEntityManagerFunctionalTestCase {
public class OneToManyBidirectionalTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
@ -40,6 +40,7 @@ public class BidirectionalOneToManyTest extends BaseEntityManagerFunctionalTestC
@Test
public void testLifecycle() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::associations-one-to-many-bidirectional-lifecycle-example[]
Person person = new Person();
Phone phone1 = new Phone( "123-456-7890" );
Phone phone2 = new Phone( "321-654-0987" );
@ -50,9 +51,11 @@ public class BidirectionalOneToManyTest extends BaseEntityManagerFunctionalTestC
entityManager.flush();
person.removePhone( phone1 );
//end::associations-one-to-many-bidirectional-lifecycle-example[]
} );
}
//tag::associations-one-to-many-bidirectional-example[]
@Entity(name = "Person")
public static class Person {
@ -138,4 +141,5 @@ public class BidirectionalOneToManyTest extends BaseEntityManagerFunctionalTestC
return Objects.hash( number );
}
}
//end::associations-one-to-many-bidirectional-example[]
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.association;
package org.hibernate.jpa.test.userguide.associations;
import java.util.ArrayList;
import java.util.List;
@ -23,7 +23,7 @@ import static org.hibernate.jpa.test.util.TransactionUtil.doInJPA;
/**
* @author Vlad Mihalcea
*/
public class UnidirectionalOneToManyTest extends BaseEntityManagerFunctionalTestCase {
public class OneToManyUnidirectionalTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
@ -36,6 +36,7 @@ public class UnidirectionalOneToManyTest extends BaseEntityManagerFunctionalTest
@Test
public void testLifecycle() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::associations-one-to-many-unidirectional-lifecycle-example[]
Person person = new Person();
Phone phone1 = new Phone( "123-456-7890" );
Phone phone2 = new Phone( "321-654-0987" );
@ -46,9 +47,11 @@ public class UnidirectionalOneToManyTest extends BaseEntityManagerFunctionalTest
entityManager.flush();
person.getPhones().remove( phone1 );
//end::associations-one-to-many-unidirectional-lifecycle-example[]
} );
}
//tag::associations-one-to-many-unidirectional-example[]
@Entity(name = "Person")
public static class Person {
@ -56,10 +59,6 @@ public class UnidirectionalOneToManyTest extends BaseEntityManagerFunctionalTest
@GeneratedValue
private Long id;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
/*@JoinTable(name = "person_phone",
joinColumns = @JoinColumn(name = "person_id", foreignKey = @ForeignKey(name = "PERSON_ID_FK")),
inverseJoinColumns = @JoinColumn(name = "phone_id", foreignKey = @ForeignKey(name = "PHONE_ID_FK"))
)*/
private List<Phone> phones = new ArrayList<>();
public Person() {
@ -94,4 +93,5 @@ public class UnidirectionalOneToManyTest extends BaseEntityManagerFunctionalTest
return number;
}
}
//end::associations-one-to-many-unidirectional-example[]
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.association;
package org.hibernate.jpa.test.userguide.associations;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
@ -26,9 +26,9 @@ import static org.hibernate.jpa.test.util.TransactionUtil.doInJPA;
/**
* @author Vlad Mihalcea
*/
public class BidirectionalOneToOneTest extends BaseEntityManagerFunctionalTestCase {
public class OneToOneBidirectionalTest extends BaseEntityManagerFunctionalTestCase {
private static final Logger log = Logger.getLogger( BidirectionalOneToOneTest.class );
private static final Logger log = Logger.getLogger( OneToOneBidirectionalTest.class );
@Override
protected Class<?>[] getAnnotatedClasses() {
@ -51,21 +51,30 @@ public class BidirectionalOneToOneTest extends BaseEntityManagerFunctionalTestCa
@Test
public void testConstraint() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::associations-one-to-one-bidirectional-lifecycle-example[]
Phone phone = new Phone( "123-456-7890" );
PhoneDetails details = new PhoneDetails( "T-Mobile", "GSM" );
phone.addDetails( details );
entityManager.persist( phone );
//end::associations-one-to-one-bidirectional-lifecycle-example[]
} );
try {
doInJPA( this::entityManagerFactory, entityManager -> {
Phone phone = new Phone( "123-456-7890" );
PhoneDetails details = new PhoneDetails( "T-Mobile", "GSM" );
phone.addDetails( details );
entityManager.persist( phone );
Phone phone = entityManager.find( Phone.class, 1L );
//tag::associations-one-to-one-bidirectional-constraint-example[]
PhoneDetails otherDetails = new PhoneDetails( "T-Mobile", "CDMA" );
otherDetails.setPhone( phone );
entityManager.persist( otherDetails );
entityManager.flush();
entityManager.clear();
//throws javax.persistence.PersistenceException: org.hibernate.HibernateException: More than one row with the given identifier was found: 1
phone = entityManager.find( Phone.class, phone.getId() );
//end::associations-one-to-one-bidirectional-constraint-example[]
phone.getDetails().getProvider();
} );
Assert.fail( "Expected: HHH000327: Error performing load command : org.hibernate.HibernateException: More than one row with the given identifier was found: 1" );
@ -75,6 +84,7 @@ public class BidirectionalOneToOneTest extends BaseEntityManagerFunctionalTestCa
}
}
//tag::associations-one-to-one-bidirectional-example[]
@Entity(name = "Phone")
public static class Phone {
@ -162,4 +172,5 @@ public class BidirectionalOneToOneTest extends BaseEntityManagerFunctionalTestCa
this.phone = phone;
}
}
//end::associations-one-to-one-bidirectional-example[]
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.association;
package org.hibernate.jpa.test.userguide.associations;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
@ -21,7 +21,7 @@ import static org.hibernate.jpa.test.util.TransactionUtil.doInJPA;
/**
* @author Vlad Mihalcea
*/
public class OneToOneTest extends BaseEntityManagerFunctionalTestCase {
public class OneToOneUnidirectionalTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
@ -43,6 +43,7 @@ public class OneToOneTest extends BaseEntityManagerFunctionalTestCase {
} );
}
//tag::associations-one-to-one-unidirectional-example[]
@Entity(name = "Phone")
public static class Phone {
@ -111,4 +112,5 @@ public class OneToOneTest extends BaseEntityManagerFunctionalTestCase {
this.technology = technology;
}
}
//end::associations-one-to-one-unidirectional-example[]
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.association;
package org.hibernate.jpa.test.userguide.associations;
import java.util.ArrayList;
import java.util.List;

View File

@ -1,58 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.collection.type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.AbstractTypeDescriptor;
import org.hibernate.type.descriptor.java.MutableMutabilityPlan;
/**
* @author Vlad Mihalcea
*/
public class CommaDelimitedStringsJavaTypeDescriptor extends AbstractTypeDescriptor<List> {
public static final String DELIMITER = ",";
public CommaDelimitedStringsJavaTypeDescriptor() {
super(
List.class,
new MutableMutabilityPlan<List>() {
@Override
protected List deepCopyNotNull(List value) {
return new ArrayList( value );
}
}
);
}
@Override
public String toString(List value) {
return ( (List<String>) value ).stream().collect( Collectors.joining( DELIMITER ) );
}
@Override
public List fromString(String string) {
List<String> values = new ArrayList<>();
Collections.addAll( values, string.split( DELIMITER ) );
return values;
}
@Override
public <X> X unwrap(List value, Class<X> type, WrapperOptions options) {
return (X) toString( value );
}
@Override
public <X> List wrap(X value, WrapperOptions options) {
return fromString( (String) value );
}
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.collection;
package org.hibernate.jpa.test.userguide.collections;
import javax.persistence.Entity;
import javax.persistence.Id;
@ -18,7 +18,7 @@ import static org.hibernate.jpa.test.util.TransactionUtil.doInJPA;
/**
* @author Vlad Mihalcea
*/
public class UnidirectionalArrayTest extends BaseEntityManagerFunctionalTestCase {
public class ArrayTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
@ -45,6 +45,7 @@ public class UnidirectionalArrayTest extends BaseEntityManagerFunctionalTestCase
} );
}
//tag::collections-array-binary-example[]
@Entity(name = "Person")
public static class Person {
@ -67,4 +68,5 @@ public class UnidirectionalArrayTest extends BaseEntityManagerFunctionalTestCase
this.phones = phones;
}
}
//end::collections-array-binary-example[]
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.collection;
package org.hibernate.jpa.test.userguide.collections;
import java.util.ArrayList;
import java.util.List;
@ -13,7 +13,7 @@ import javax.persistence.Id;
import org.hibernate.annotations.Type;
import org.hibernate.cfg.Configuration;
import org.hibernate.jpa.test.userguide.collection.type.CommaDelimitedStringsType;
import org.hibernate.jpa.test.userguide.collections.type.CommaDelimitedStringsType;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
@ -41,14 +41,14 @@ public class BasicTypeCollectionTest extends BaseCoreFunctionalTestCase {
doInHibernate( this::sessionFactory, session -> {
Person person = new Person();
person.id = 1L;
session.persist( person );
//tag::collections-comma-delimited-collection-lifecycle-example[]
person.phones.add( "027-123-4567" );
person.phones.add( "028-234-9876" );
session.persist( person );
} );
doInHibernate( this::sessionFactory, session -> {
Person person = session.get( Person.class, 1L );
log.infov( "Remove one element" );
session.flush();
person.getPhones().remove( 0 );
//end::collections-comma-delimited-collection-lifecycle-example[]
} );
}
@ -61,6 +61,7 @@ public class BasicTypeCollectionTest extends BaseCoreFunctionalTestCase {
return configuration;
}
//tag::collections-comma-delimited-collection-example[]
@Entity(name = "Person")
public static class Person {
@ -74,4 +75,5 @@ public class BasicTypeCollectionTest extends BaseCoreFunctionalTestCase {
return phones;
}
}
//end::collections-comma-delimited-collection-example[]
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.collection;
package org.hibernate.jpa.test.userguide.collections;
import java.util.ArrayList;
import java.util.List;
@ -14,7 +14,6 @@ import javax.persistence.Id;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Assert;
import org.junit.Test;
import org.jboss.logging.Logger;
@ -50,10 +49,12 @@ public class BasicTypeElementCollectionTest extends BaseEntityManagerFunctionalT
@Test
public void testProxies() {
doInJPA( this::entityManagerFactory, entityManager -> {
Person person = entityManager.find( Person.class, 1L );
Assert.assertEquals( 2, person.getPhones().size() );
try {
//tag::collections-collection-proxy-usage-example[]
Person person = entityManager.find( Person.class, 1L );
//Throws java.lang.ClassCastException: org.hibernate.collection.internal.PersistentBag cannot be cast to java.util.ArrayList
ArrayList<String> phones = (ArrayList<String>) person.getPhones();
//end::collections-collection-proxy-usage-example[]
}
catch (Exception expected) {
log.error( "Failure", expected );
@ -66,17 +67,22 @@ public class BasicTypeElementCollectionTest extends BaseEntityManagerFunctionalT
doInJPA( this::entityManagerFactory, entityManager -> {
Person person = entityManager.find( Person.class, 1L );
log.info( "Clear element collection and add element" );
//tag::collections-value-type-collection-lifecycle-example[]
person.getPhones().clear();
person.getPhones().add( "123-456-7890" );
person.getPhones().add( "456-000-1234" );
//end::collections-value-type-collection-lifecycle-example[]
} );
doInJPA( this::entityManagerFactory, entityManager -> {
Person person = entityManager.find( Person.class, 1L );
log.info( "Remove one element" );
//tag::collections-value-type-collection-remove-example[]
person.getPhones().remove( 0 );
//end::collections-value-type-collection-remove-example[]
} );
}
//tag::collections-collection-proxy-entity-example[]
@Entity(name = "Person")
public static class Person {
@ -90,4 +96,5 @@ public class BasicTypeElementCollectionTest extends BaseEntityManagerFunctionalT
return phones;
}
}
//end::collections-collection-proxy-entity-example[]
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.collection;
package org.hibernate.jpa.test.userguide.collections;
import java.util.ArrayList;
import java.util.List;
@ -47,7 +47,9 @@ public class BasicTypeOrderColumnElementCollectionTest extends BaseEntityManager
doInJPA( this::entityManagerFactory, entityManager -> {
Person person = entityManager.find( Person.class, 1L );
log.info( "Remove one element" );
//tag::collections-value-type-collection-order-column-remove-example[]
person.getPhones().remove( 0 );
//end::collections-value-type-collection-order-column-remove-example[]
} );
}
@ -57,9 +59,11 @@ public class BasicTypeOrderColumnElementCollectionTest extends BaseEntityManager
@Id
private Long id;
//tag::collections-value-type-collection-order-column-remove-entity-example[]
@ElementCollection
@OrderColumn(name = "order_id")
private List<String> phones = new ArrayList<>();
//end::collections-value-type-collection-order-column-remove-entity-example[]
public List<String> getPhones() {
return phones;

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.collection;
package org.hibernate.jpa.test.userguide.collections;
import java.util.ArrayList;
import java.util.List;
@ -53,8 +53,11 @@ public class BidirectionalBagOrphanRemovalTest extends BaseEntityManagerFunction
@Id
private Long id;
//tag::collections-bidirectional-bag-orphan-removal-example[]
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Phone> phones = new ArrayList<>();
//end::collections-bidirectional-bag-orphan-removal-example[]
public Person() {
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.collection;
package org.hibernate.jpa.test.userguide.collections;
import java.util.ArrayList;
import java.util.List;
@ -41,13 +41,16 @@ public class BidirectionalBagTest extends BaseEntityManagerFunctionalTestCase {
doInJPA( this::entityManagerFactory, entityManager -> {
Person person = new Person( 1L );
entityManager.persist( person );
//tag::collections-bidirectional-bag-lifecycle-example[]
person.addPhone( new Phone( 1L, "landline", "028-234-9876" ) );
person.addPhone( new Phone( 2L, "mobile", "072-122-9876" ) );
entityManager.flush();
person.removePhone( person.getPhones().get( 0 ) );
//end::collections-bidirectional-bag-lifecycle-example[]
} );
}
//tag::collections-bidirectional-bag-example[]
@Entity(name = "Person")
public static class Person {
@ -139,4 +142,5 @@ public class BidirectionalBagTest extends BaseEntityManagerFunctionalTestCase {
return Objects.hash( number );
}
}
//end::collections-bidirectional-bag-example[]
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.collection;
package org.hibernate.jpa.test.userguide.collections;
import java.util.Comparator;
import java.util.Objects;

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.collection;
package org.hibernate.jpa.test.userguide.collections;
import java.sql.Timestamp;
import java.time.LocalDateTime;
@ -61,6 +61,7 @@ public class BidirectionalMapTest extends BaseEntityManagerFunctionalTestCase {
MOBILE
}
//tag::collections-map-bidirectional-example[]
@Entity(name = "Person")
public static class Person {
@ -133,4 +134,5 @@ public class BidirectionalMapTest extends BaseEntityManagerFunctionalTestCase {
this.person = person;
}
}
//end::collections-map-bidirectional-example[]
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.collection;
package org.hibernate.jpa.test.userguide.collections;
import java.util.ArrayList;
import java.util.List;
@ -57,9 +57,12 @@ public class BidirectionalOrderByListTest extends BaseEntityManagerFunctionalTes
@Id
private Long id;
//tag::collections-bidirectional-ordered-list-order-by-example[]
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
@OrderBy("number")
private List<Phone> phones = new ArrayList<>();
//end::collections-bidirectional-ordered-list-order-by-example[]
public Person() {
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.collection;
package org.hibernate.jpa.test.userguide.collections;
import java.util.ArrayList;
import java.util.List;
@ -57,9 +57,12 @@ public class BidirectionalOrderColumnListTest extends BaseEntityManagerFunctiona
@Id
private Long id;
//tag::collections-bidirectional-ordered-list-order-column-example[]
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
@OrderColumn(name = "order_id")
private List<Phone> phones = new ArrayList<>();
//end::collections-bidirectional-ordered-list-order-column-example[]
public Person() {
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.collection;
package org.hibernate.jpa.test.userguide.collections;
import java.util.HashSet;
import java.util.Objects;
@ -59,11 +59,13 @@ public class BidirectionalSetTest extends BaseEntityManagerFunctionalTestCase {
} );
}
//tag::collections-bidirectional-set-example[]
@Entity(name = "Person")
public static class Person {
@Id
private Long id;
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
private Set<Phone> phones = new HashSet<>();
@ -150,4 +152,5 @@ public class BidirectionalSetTest extends BaseEntityManagerFunctionalTestCase {
return Objects.hash( number );
}
}
//end::collections-bidirectional-set-example[]
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.collection;
package org.hibernate.jpa.test.userguide.collections;
import java.util.Objects;
import java.util.Set;
@ -71,9 +71,12 @@ public class BidirectionalSortedSetTest extends BaseEntityManagerFunctionalTestC
@Id
private Long id;
//tag::collections-bidirectional-sorted-set-example[]
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
@SortNatural
private SortedSet<Phone> phones = new TreeSet<>();
//end::collections-bidirectional-sorted-set-example[]
public Person() {
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.collection;
package org.hibernate.jpa.test.userguide.collections;
import java.util.Date;
import java.util.HashMap;
@ -43,8 +43,14 @@ public class ElementCollectionMapTest extends BaseEntityManagerFunctionalTestCas
public void testLifecycle() {
doInJPA( this::entityManagerFactory, entityManager -> {
Person person = new Person( 1L );
person.getPhoneRegister().put( new Phone( PhoneType.LAND_LINE, "028-234-9876" ), new Date() );
person.getPhoneRegister().put( new Phone( PhoneType.MOBILE, "072-122-9876" ), new Date() );
//tag::collections-map-value-type-entity-key-add-example[]
person.getPhoneRegister().put(
new Phone( PhoneType.LAND_LINE, "028-234-9876" ), new Date()
);
person.getPhoneRegister().put(
new Phone( PhoneType.MOBILE, "072-122-9876" ), new Date()
);
//end::collections-map-value-type-entity-key-add-example[]
entityManager.persist( person );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
@ -54,6 +60,7 @@ public class ElementCollectionMapTest extends BaseEntityManagerFunctionalTestCas
} );
}
//tag::collections-map-value-type-entity-key-example[]
public enum PhoneType {
LAND_LINE,
MOBILE
@ -106,4 +113,5 @@ public class ElementCollectionMapTest extends BaseEntityManagerFunctionalTestCas
return number;
}
}
//end::collections-map-value-type-entity-key-example[]
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.userguide.collection;
package org.hibernate.jpa.test.userguide.collections;
import java.util.ArrayList;
import java.util.List;
@ -36,12 +36,15 @@ public class EmbeddableTypeElementCollectionTest extends BaseEntityManagerFuncti
doInJPA( this::entityManagerFactory, entityManager -> {
Person person = new Person();
person.id = 1L;
//tag::collections-embeddable-type-collection-lifecycle-example[]
person.getPhones().add( new Phone( "landline", "028-234-9876" ) );
person.getPhones().add( new Phone( "mobile", "072-122-9876" ) );
//end::collections-embeddable-type-collection-lifecycle-example[]
entityManager.persist( person );
} );
}
//tag::collections-embeddable-type-collection-lifecycle-entity-example[]
@Entity(name = "Person")
public static class Person {
@ -79,4 +82,5 @@ public class EmbeddableTypeElementCollectionTest extends BaseEntityManagerFuncti
return number;
}
}
//end::collections-embeddable-type-collection-lifecycle-entity-example[]
}

Some files were not shown because too many files have changed in this diff Show More