From e099b06c6d02fe35a3e38135f787d499ae7938d7 Mon Sep 17 00:00:00 2001 From: Gavin Date: Thu, 7 Sep 2023 14:00:13 +0200 Subject: [PATCH] new section on Derived Identity --- .../main/asciidoc/introduction/Advanced.adoc | 5 +- .../main/asciidoc/introduction/Entities.adoc | 2 + .../main/asciidoc/introduction/Mapping.adoc | 148 +++++++++++++++++- 3 files changed, 151 insertions(+), 4 deletions(-) diff --git a/documentation/src/main/asciidoc/introduction/Advanced.adoc b/documentation/src/main/asciidoc/introduction/Advanced.adoc index 27f3f17e09..cc52e24e4d 100644 --- a/documentation/src/main/asciidoc/introduction/Advanced.adoc +++ b/documentation/src/main/asciidoc/introduction/Advanced.adoc @@ -974,7 +974,7 @@ class Book { @FetchProfileOverride(profile = Book_.PROFILE_EAGER_BOOK, mode = JOIN) Set authors; - ... + ... } ---- [source,java] @@ -1013,7 +1013,7 @@ class Book { mode = SUBSELECT) Set authors; - ... + ... } ---- @@ -1045,6 +1045,7 @@ But Hibernate offers alternatives that we think are more compelling most of the The one and only advantage unique to fetch profiles is that they let us very selectively request subselect fetching. We can't do that with entity graphs, and we can't do it with HQL. +[%unbreakable] [TIP] ==== There's a special built-in fetch profile named `org.hibernate.defaultProfile` which is defined as the profile with `@FetchProfileOverride(mode=JOIN)` applied to every eager `@ManyToOne` or `@OneToOne` association. diff --git a/documentation/src/main/asciidoc/introduction/Entities.adoc b/documentation/src/main/asciidoc/introduction/Entities.adoc index 60e17e2663..67155d9011 100644 --- a/documentation/src/main/asciidoc/introduction/Entities.adoc +++ b/documentation/src/main/asciidoc/introduction/Entities.adoc @@ -1047,6 +1047,8 @@ The `Publisher.books` field is called the _unowned_ side of the association. Now, we passionately _hate_ the stringly-typed `mappedBy` reference to the owning side of the association. Thankfully, the <> gives us a way to make it a bit more typesafe: + +[[mapped-by-metamodel]] [source,java] ---- @OneToMany(mappedBy=Book_.PUBLISHER) // get used to doing it this way! diff --git a/documentation/src/main/asciidoc/introduction/Mapping.adoc b/documentation/src/main/asciidoc/introduction/Mapping.adoc index 74988eb9c1..951d959d25 100644 --- a/documentation/src/main/asciidoc/introduction/Mapping.adoc +++ b/documentation/src/main/asciidoc/introduction/Mapping.adoc @@ -174,7 +174,7 @@ is a bad idea, since it's impossible to create a foreign key constraint that tar The following annotations specify exactly how elements of the domain model map to tables of the relational model: .Annotations for mapping tables -[%autowidth.stretch] +[%breakable,cols="25,~"] |=== | Annotation | Purpose @@ -334,7 +334,7 @@ To better understand these annotations, we must first discuss column mappings in These annotations specify how elements of the domain model map to columns of tables in the relational model: .Annotations for mapping columns -[%autowidth.stretch] +[%breakable,cols="25,~"] |=== | Annotation | Purpose @@ -836,6 +836,150 @@ class Order { } ---- +[[derived-identity]] +=== Derived Identity + +An entity has a _derived identity_ if it inherits part of its primary key from an associated "parent" entity. +We've already met a kind of degenerate case of _derived identity_ when we talked about <>. + +But a `@ManyToOne` association may also form part of a derived identity. +That is to say, there could be a foreign key column or columns included as part of the composite primary key. +There's three different ways to represent this situation on the Java side of things: + +- using `@IdClass` without `@MapsId`, +- using `@IdClass` with `@MapsId`, or +- using `@EmbeddedId` with `@MapsId`. + +Let's suppose we have a `Parent` entity class defined as follows: + +[source,java] +---- +@Entity +class Parent { + @Id + Long parentId; + + ... +} +---- + +[discrete] +==== First way + +In the first, slightly simpler approach, we define an `@IdClass`: + +[source,java] +---- +class DerivedId { + Long parent; + String childId; + + // constructors, equals, hashcode, etc + ... +} +---- + +And a `Child` entity class with a `@ManyToOne` association annotated `@Id`: + +[source,java] +---- +@Entity +@IdClass(DerivedId.class) +class Child { + @Id + String childId; + + @Id @ManyToOne + @JoinColumn(name="parentId") + Parent parent; + + ... +} +---- + +Then the primary key of the `Child` table comprises the columns `(childId,parentId)`. + +[discrete] +==== Second way + +This is fine, but sometimes it's nice to have a field for each element of the primary key. +We may use the `@MapsId` annotation we met <>: + +[source,java] +---- +@Entity +@IdClass(DerivedId.class) +class Child { + @Id + Long parentId; + @Id + String childId; + + @ManyToOne + @MapsId(Child_.PARENT_ID) // typesafe reference to Child.parentId + @JoinColumn(name="parentId") + Parent parent; + + ... +} +---- + +We're using the approach we saw <> to refer to the `parentId` property of `Child` in a typesafe way. + +Note that we must place column mapping information on the association annotated `@MapsId`, not on the `@Id` field. + +We must slightly modify our `@IdClass` so that field names align: + +[source,java] +---- +class DerivedId { + Long parentId; + String childId; + + // constructors, equals, hashcode, etc + ... +} +---- + +[discrete] +==== Third way + +The third alternative is to redefine our `@IdClass` as an `@Embeddable`. +We don't actually need to change the `DerivedId` class, but we do need to add the annotation. + +[source,java] +---- +@Embeddable +class DerivedId { + Long parentId; + String childId; + + // constructors, equals, hashcode, etc + ... +} +---- + +Then we may use `@EmbeddedId` in `Child`: + +[source,java] +---- +@Entity +class Child { + @EmbeddedId + DerivedId id; + + @ManyToOne + @MapsId(DerivedId_.PARENT_ID) // typesafe reference to DerivedId.parentId + @JoinColumn(name="parentId") + Parent parent; + + ... +} +---- + +The <> between `@IdClass` and `@EmbeddedId` boils down to taste. +The `@EmbeddedId` is perhaps a little DRYer. + [[constraints]] === Adding constraints