one to one
This commit is contained in:
parent
f012afe95f
commit
33a3af7abc
|
@ -34,6 +34,12 @@ An entity must:
|
||||||
|
|
||||||
On the other hand, the entity class may be either concrete or `abstract`, and it may have any number of additional constructors.
|
On the other hand, the entity class may be either concrete or `abstract`, and it may have any number of additional constructors.
|
||||||
|
|
||||||
|
[TIP]
|
||||||
|
.Inner entity classes
|
||||||
|
====
|
||||||
|
An entity class may be a `static` inner class.
|
||||||
|
====
|
||||||
|
|
||||||
Every entity class must be annotated `@Entity`.
|
Every entity class must be annotated `@Entity`.
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
|
@ -694,19 +700,32 @@ The `@ManyToOne` annotation marks the "one" side of the association, and so a un
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
class Book {
|
class Book {
|
||||||
...
|
@Id @GeneratedValue
|
||||||
@ManyToOne
|
Long id;
|
||||||
|
|
||||||
|
@ManyToOne(fetch=LAZY)
|
||||||
Publisher publisher;
|
Publisher publisher;
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
|
[TIP]
|
||||||
|
.Almost all associations should be lazy
|
||||||
|
====
|
||||||
|
A very unfortunate misfeature of JPA is that `@ManyToOne` associations are fetched eagerly by default.
|
||||||
|
This is almost never what we want.
|
||||||
|
The only scenario in which `fetch=EAGER` makes sense is if we think there's always a _very_ high probability that the associated object will be found in the second-level cache.
|
||||||
|
Whenever this isn't the case, remember to explicitly specify `fetch=LAZY`.
|
||||||
|
====
|
||||||
|
|
||||||
To make this association bidirectional, we need to add a collection-valued attribute to the `Publisher` class, and annotate it `@OneToMany`, using the `mappedBy` member to refer back to `Book.publisher`.
|
To make this association bidirectional, we need to add a collection-valued attribute to the `Publisher` class, and annotate it `@OneToMany`, using the `mappedBy` member to refer back to `Book.publisher`.
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
class Publisher {
|
class Publisher {
|
||||||
...
|
@Id @GeneratedValue
|
||||||
|
Long id;
|
||||||
|
|
||||||
@OneToMany(mappedBy="publisher")
|
@OneToMany(mappedBy="publisher")
|
||||||
Set<Book> books;
|
Set<Book> books;
|
||||||
...
|
...
|
||||||
|
@ -761,6 +780,92 @@ Now? I guess we're happy to let you guys decide.
|
||||||
In hindsight, we could have done more to make clear that this was always a viable option.
|
In hindsight, we could have done more to make clear that this was always a viable option.
|
||||||
====
|
====
|
||||||
|
|
||||||
|
[[one-to-one-fk]]
|
||||||
|
=== One to one (first way)
|
||||||
|
|
||||||
|
The simplest sort of one to one association is almost exactly line a `@ManyToOne` association, except that it maps to a foreign key column with a `UNIQUE` constraint.
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
@Entity
|
||||||
|
static class Author {
|
||||||
|
@Id @GeneratedValue
|
||||||
|
Long id;
|
||||||
|
|
||||||
|
@OneToOne(optional=false, fetch=LAZY)
|
||||||
|
Person author;
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
We can make this association bidirectional by adding a reference back to the `Author` in the `Person` entity:
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
@Entity
|
||||||
|
static class Person {
|
||||||
|
@Id @GeneratedValue
|
||||||
|
Long id;
|
||||||
|
|
||||||
|
@OneToOne(mappedBy = "person")
|
||||||
|
Author author;
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
.Lazy fetching for one to one associations
|
||||||
|
====
|
||||||
|
Notice that we did not declare the unowned end of the association `fetch=LAZY`.
|
||||||
|
That's because:
|
||||||
|
|
||||||
|
1. not every `Person` has an associated `Author`, and
|
||||||
|
2. the foreign key is held in the table mapped by `Author`, not in the table mapped by `Person`.
|
||||||
|
|
||||||
|
Therefore, Hibernate can't tell if the reference from `Person` to `Author` is `null` without fetching the associated `Author`.
|
||||||
|
|
||||||
|
On the other hand, if _every_ `Person` was an `Author`, that is, if the association were non-`optional`, we would not have to consider the possibility of `null` references, and we would map it like this:
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
@OneToOne(optional=false, mappedBy = "person", fetch=LAZY)
|
||||||
|
Author author;
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
This is not the only sort of one to one association.
|
||||||
|
|
||||||
|
[[one-to-one-pk]]
|
||||||
|
=== One to one (second way)
|
||||||
|
|
||||||
|
An arguably more elegant way to represent such a relationship is to share a primary key between the two tables.
|
||||||
|
That is, the foreign key would be the primary key.
|
||||||
|
|
||||||
|
To use this approach, the `Author` class must be annotated like this:
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
@Entity
|
||||||
|
static class Author {
|
||||||
|
@Id
|
||||||
|
Long id;
|
||||||
|
|
||||||
|
@OneToOne(optional=false, fetch=LAZY)
|
||||||
|
@MapsId
|
||||||
|
Person author;
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Notice that the `@Id` attribute is no longer a `@GeneratedValue` and, instead, the `author` association is annotated `@MapsId`.
|
||||||
|
This lets Hibernate know that the association to `Person` is the source of primary key values for `Author`.
|
||||||
|
That is, that the foreign key column referring to the `Author` table is also the primary key of the `Person` table.
|
||||||
|
|
||||||
|
The `Person` class does not change.
|
||||||
|
If the association is bidirectional, we annotate the unowned side `@OneToOne(mappedBy = "person")` just as before.
|
||||||
|
|
||||||
|
|
||||||
[[equals-and-hash]]
|
[[equals-and-hash]]
|
||||||
|
|
Loading…
Reference in New Issue