new section on Derived Identity
This commit is contained in:
parent
c014abb0d5
commit
e099b06c6d
|
@ -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.
|
||||
|
|
|
@ -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 <<metamodel-generator, Metamodel Generator>> 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!
|
||||
|
|
|
@ -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 <<one-to-one-pk,one-to-one associations with a shared primary key>>.
|
||||
|
||||
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 <<one-to-one-pk,earlier>>:
|
||||
|
||||
[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 <<mapped-by-metamodel,previously>> 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 <<composite-identifiers,choice>> between `@IdClass` and `@EmbeddedId` boils down to taste.
|
||||
The `@EmbeddedId` is perhaps a little DRYer.
|
||||
|
||||
[[constraints]]
|
||||
=== Adding constraints
|
||||
|
||||
|
|
Loading…
Reference in New Issue