Skip to content

Commit 6b7248c

Browse files
committed
HHH-18728 - Allow mixed discriminator-value mappings for ANY
HHH-18729 - Allow custom strategy for implicit discriminator-value determination for ANY
1 parent c8dab53 commit 6b7248c

36 files changed

+976
-684
lines changed

documentation/src/main/asciidoc/userguide/chapters/domain/associations.adoc

Lines changed: 75 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
:root-project-dir: ../../../../../../..
44
:core-project-dir: {root-project-dir}/hibernate-core
55
:example-dir-association: {core-project-dir}/src/test/java/org/hibernate/orm/test/associations
6+
:example-dir-any: {core-project-dir}/src/test/java/org/hibernate/orm/test/any
67
:extrasdir: extras/associations
78

89
Associations describe how two or more entities form a relationship based on a database joining semantics.
@@ -559,7 +560,7 @@ The `@Any` mapping is useful to emulate a unidirectional `@ManyToOne` associatio
559560

560561
Because the `@Any` mapping defines a polymorphic association to classes from multiple tables,
561562
this association type requires the FK column which provides the associated parent identifier and
562-
a metadata information for the associated entity type.
563+
a discriminator which identifies the associated entity type.
563564

564565
[NOTE]
565566
====
@@ -568,16 +569,33 @@ This is not the usual way of mapping polymorphic associations and you should use
568569

569570
To map such an association, Hibernate needs to understand 3 things:
570571

571-
1. The column and mapping for the discriminator
572-
2. The column and mapping for the key
573-
3. The mapping between discriminator values and entity classes
572+
1. The column and mapping for the <<associations-any-discriminator,discriminator>>
573+
2. The column and mapping for the <<associations-any-key,key>>
574+
3. The mapping between discriminator values and entity types which may be <<associations-any-explicit-discriminator,explicit>>
575+
<<associations-any-implicit-discriminator,implicit>> or <<associations-any-mixed-discriminator,mixed>>.
576+
577+
For the rest of this discussion, consider the following model which will be the target types for the `@Any` associations:
578+
579+
[[associations-any-target-example]]
580+
.`Payment` class hierarchy
581+
====
582+
[source, java, indent=0]
583+
----
584+
include::{example-dir-any}/discriminator/Payment.java[tags=associations-any-example]
585+
586+
include::{example-dir-any}/discriminator/CardPayment.java[tags=associations-any-example]
587+
588+
include::{example-dir-any}/discriminator/CashPayment.java[tags=associations-any-example]
589+
590+
include::{example-dir-any}/discriminator/CheckPayment.java[tags=associations-any-example]
591+
----
592+
====
574593

575594

576595
[[associations-any-discriminator]]
577596
===== The discriminator
578597

579-
The discriminator of an any-style association holds the value that indicates which entity is
580-
referred to by a row.
598+
The discriminator is the value that indicates which entity is referred to by a row.
581599

582600
Its "column" can be specified with either `@Column` or `@Formula`. The mapping type can be influenced by any of:
583601

@@ -601,114 +619,105 @@ type can be influenced by any of:
601619
4. `@AnyKeyJdbcTypeCode`
602620

603621

604-
[[associations-any-values]]
605-
===== The discriminator value mappings
606622

607-
`@AnyDiscriminatorValue` is used to map the discriminator values to the corresponding entity classes
623+
[[associations-any-explicit-discriminator]]
624+
===== Explicit discriminator mappings
608625

626+
Explicit discriminator mappings are defined using one-or-more `@AnyDiscriminatorValue` annotations. E.g.
609627

610-
[[associations-any-property]]
611-
==== Example using @Any mapping
612628

613-
For this example, consider the following `Property` class hierarchy:
614-
615-
[[associations-any-property-example]]
616-
.`Property` class hierarchy
629+
[[associations-any-discriminator-explicit-example]]
630+
.Explicit @AnyDiscriminatorValue annotations
617631
====
618632
[source, java, indent=0]
619633
----
620-
include::{example-dir-association}/any/Property.java[tags=associations-any-property-example]
621-
622-
include::{example-dir-association}/any/IntegerProperty.java[tags=associations-any-property-example]
623-
624-
include::{example-dir-association}/any/StringProperty.java[tags=associations-any-property-example]
634+
include::{example-dir-any}/discriminator/explicit/Order.java[tags=associations-any-explicit-discriminator-example]
625635
----
626636
====
627637

628-
A `PropertyHolder` entity defines an attribute of type `Property`:
638+
Here, we map 2 explicit discriminator value mappings:
639+
640+
1. `CARD` <-> `CardPayment`
641+
2. `CHECK` <-> `CheckPayment`
642+
643+
Notice that `CashPayment` is not explicitly mapped. An attempt to use `CashPayment` for this attribute will result
644+
in an exception.
645+
629646

630-
[[associations-any-example]]
631-
.`@Any` mapping usage
647+
[[associations-any-implicit-discriminator]]
648+
===== Implicit discriminator mappings
649+
650+
Implicit discriminator mappings define no `@AnyDiscriminatorValue` annotations. E.g.
651+
652+
[[associations-any-discriminator-implicit-example]]
653+
.Implicit @Any discriminator mappings
632654
====
633655
[source, java, indent=0]
634656
----
635-
include::{example-dir-association}/any/PropertyHolder.java[tags=associations-any-example]
636-
----
637-
638-
[source, SQL, indent=0]
639-
----
640-
include::{extrasdir}/associations-any-example.sql[]
657+
include::{example-dir-any}/discriminator/implicit/Order.java[tags=associations-any-implicit-discriminator-example]
641658
----
642659
====
643660

644-
`PropertyHolder#property` can refer to either `StringProperty` or `IntegerProperty` references, as indicated
645-
by the associated discriminator according to the `@DiscriminatorValue` annotations.
661+
Here all `Payment` subtypes are allowed. By default Hibernate will use the entity's full-name (which is generally the class's FQN).
646662

647-
As you can see, there are two columns used to reference a `Property` instance: `property_id` and `property_type`.
648-
The `property_id` is used to match the `id` column of either the `string_property` or `integer_property` tables,
649-
while the `property_type` is used to match the `string_property` or the `integer_property` table.
663+
Hibernate also offers a `@AnyDiscriminatorImplicitValues` annotation which allows configuration of how this implicit
664+
mapping works. E.g., to use the entity's short-name instead of the full-name -
650665

651-
To see the `@Any` annotation in action, consider the next examples.
652666

653-
If we persist an `IntegerProperty` as well as a `StringProperty` entity, and associate
654-
the `StringProperty` entity with a `PropertyHolder`,
655-
Hibernate will generate the following SQL queries:
656-
657-
[[associations-any-persist-example]]
658-
.`@Any` mapping persist example
667+
[[associations-any-discriminator-implicit-short-example]]
668+
.Implicit @Any discriminator mappings (short name)
659669
====
660670
[source, java, indent=0]
661671
----
662-
include::{example-dir-association}/any/AnyTest.java[tags=associations-any-persist-example]
672+
include::{example-dir-any}/discriminator/implicit/Order.java[tags=associations-any-implicit-discriminator-short-example]
663673
----
674+
====
664675

665-
[source, SQL, indent=0]
666-
----
667-
include::{extrasdir}/associations-any-persist-example.sql[]
668-
----
676+
[NOTE]
669677
====
678+
`@AnyDiscriminatorImplicitValues` also offers the ability to define a custom strategy for determining the
679+
discriminator-value <-> entity-type mapping, but its use is not covered here.
680+
====
681+
682+
683+
[[associations-any-mixed-discriminator]]
684+
===== Mixed discriminator mappings
670685

671-
When fetching the `PropertyHolder` entity and navigating its `property` association,
672-
Hibernate will fetch the associated `StringProperty` entity like this:
686+
A mixed strategy combines `@AnyDiscriminatorValue` and `@AnyDiscriminatorImplicitValues`. Mappings
687+
explicitly defined using `@AnyDiscriminatorValue` take precedence. E.g.
673688

674-
[[associations-any-query-example]]
675-
.`@Any` mapping query example
689+
690+
[[associations-any-discriminator-mixed-example]]
691+
.Mixed @Any discriminator mappings (short name)
676692
====
677693
[source, java, indent=0]
678694
----
679-
include::{example-dir-association}/any/AnyTest.java[tags=associations-any-query-example]
680-
----
681-
682-
[source, SQL, indent=0]
683-
----
684-
include::{extrasdir}/associations-any-query-example.sql[]
695+
include::{example-dir-any}/discriminator/mixed/Order.java[tags=associations-any-mixed-discriminator-short-example]
685696
----
686697
====
687698

688699

700+
689701
[[associations-any-meta-annotations]]
690702
===== Using meta-annotations
691703

692704
As mentioned in <<basic-mapping>>, Hibernate's ANY-related annotations can be composed using meta-annotations
693705
to re-use ANY mapping details.
694706

695-
Looking back at <<associations-any-example>>, we can see how cumbersome it would be to duplicate that
696-
information every time `Property` is mapped in the domain model. This description can also be moved
707+
Given all the details needed to define an ANY mapping, we can see how cumbersome it would be to duplicate that
708+
information every time `Payment` is mapped in the domain model. This description can also be moved
697709
into a single annotation that we can apply in each usage.
698710

699711

700712
[[associations-any-composed-example]]
701-
.`@Any` mapping usage
713+
.`@Any` mapping with meta-annotation
702714
====
703715
[source, java, indent=0]
704716
----
705-
include::{example-dir-association}/any/PropertyHolder2.java[tags=associations-any-def-example]
717+
include::{example-dir-any}/discriminator/meta/Order.java[tags=associations-any-discriminator-meta-example]
706718
----
707719
====
708720

709-
Though the mapping has been "simplified", the mapping works exactly as shown in <<associations-any-example>>.
710-
711-
712721

713722

714723
[[associations-many-to-any]]
@@ -724,16 +733,16 @@ The mapping details are the same between `@Any` and `@ManyToAny` except for:
724733
of just `@JoinColumn`
725734

726735

727-
In the following example, the `PropertyRepository` entity has a collection of `Property` entities.
736+
In the following example, the `Loan` entity has a collection of `Payments` objects.
728737

729-
The `repository_properties` link table holds the associations between `PropertyRepository` and `Property` entities.
738+
The `loan_payments` table holds the associations between `Loan` and `Payment` references.
730739

731740
[[associations-many-to-any-example]]
732741
.`@ManyToAny` mapping usage
733742
====
734743
[source, java, indent=0]
735744
----
736-
include::{example-dir-association}/any/PropertyRepository.java[tags=associations-many-to-any-example]
745+
include::{example-dir-any}/discriminator/many/Loan.java[tags=associations-many-to-any-example]
737746
----
738747
739748
[source, SQL, indent=0]
@@ -742,43 +751,6 @@ include::{extrasdir}/associations-many-to-any-example.sql[]
742751
----
743752
====
744753

745-
To see the `@ManyToAny` annotation in action, consider the next examples.
746-
747-
If we persist an `IntegerProperty` as well as a `StringProperty` entity,
748-
and associate both of them with a `PropertyRepository` parent entity,
749-
Hibernate will generate the following SQL queries:
750-
751-
[[associations-many-to-any-persist-example]]
752-
.`@ManyToAny` mapping persist example
753-
====
754-
[source, java, indent=0]
755-
----
756-
include::{example-dir-association}/any/ManyToAnyTest.java[tags=associations-many-to-any-persist-example]
757-
----
758-
759-
[source, SQL, indent=0]
760-
----
761-
include::{extrasdir}/associations-many-to-any-persist-example.sql[]
762-
----
763-
====
764-
765-
When fetching the `PropertyRepository` entity and navigating its `properties` association,
766-
Hibernate will fetch the associated `IntegerProperty` and `StringProperty` entities like this:
767-
768-
[[associations-many-to-any-query-example]]
769-
.`@ManyToAny` mapping query example
770-
====
771-
[source, java, indent=0]
772-
----
773-
include::{example-dir-association}/any/ManyToAnyTest.java[tags=associations-many-to-any-query-example]
774-
----
775-
776-
[source, SQL, indent=0]
777-
----
778-
include::{extrasdir}/associations-many-to-any-query-example.sql[]
779-
----
780-
====
781-
782754

783755

784756
[[associations-JoinFormula]]

documentation/src/main/asciidoc/userguide/chapters/domain/extras/associations/associations-any-example.sql

Lines changed: 0 additions & 6 deletions
This file was deleted.

documentation/src/main/asciidoc/userguide/chapters/domain/extras/associations/associations-any-persist-example.sql

Lines changed: 0 additions & 11 deletions
This file was deleted.

documentation/src/main/asciidoc/userguide/chapters/domain/extras/associations/associations-any-query-example.sql

Lines changed: 0 additions & 12 deletions
This file was deleted.
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
CREATE TABLE property_repository (
1+
CREATE TABLE loans (
22
id BIGINT NOT NULL,
3+
...,
34
PRIMARY KEY ( id )
45
)
56

6-
CREATE TABLE repository_properties (
7-
repository_id BIGINT NOT NULL,
8-
property_type VARCHAR(255),
9-
property_id BIGINT NOT NULL
7+
CREATE TABLE loan_payments (
8+
loan_fk BIGINT NOT NULL,
9+
payment_type VARCHAR(255),
10+
payment_fk BIGINT NOT NULL
1011
)

documentation/src/main/asciidoc/userguide/chapters/domain/extras/associations/associations-many-to-any-persist-example.sql

Lines changed: 0 additions & 15 deletions
This file was deleted.

documentation/src/main/asciidoc/userguide/chapters/domain/extras/associations/associations-many-to-any-query-example.sql

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)