show off typesafety with the metamodel and new API of NaturalIdLoadAccess
This commit is contained in:
parent
76fa597d1b
commit
f881c5243f
|
@ -951,17 +951,17 @@ There are three annotations for mapping associations: `@ManyToOne`, `@OneToMany`
|
||||||
They share some common annotation members:
|
They share some common annotation members:
|
||||||
|
|
||||||
.Association-defining annotation members
|
.Association-defining annotation members
|
||||||
[cols="14,~,35"]
|
[cols="13,~,35"]
|
||||||
|===
|
|===
|
||||||
| Member | Interpretation | Default value
|
| Member | Interpretation | Default value
|
||||||
|
|
||||||
| `cascade` | Persistence operations which should <<cascade,cascade>> to the associated entity; a list of ``CascadeType``s | `{}`
|
| `cascade` | Persistence operations which should <<cascade,cascade>> to the associated entity; a list of ``CascadeType``s | `{}`
|
||||||
| `fetch` | Whether the association is eagerly <<association-fetching,fetched>> or may be <<proxies-and-lazy-fetching,proxied>>
|
| `fetch` | Whether the association is <<entity-graph,eagerly>> <<association-fetching,fetched>> or may be <<proxies-and-lazy-fetching,proxied>>
|
||||||
a|
|
a|
|
||||||
- `LAZY` for `@OneToMany` and `@ManyToMany`
|
- `LAZY` for `@OneToMany` and `@ManyToMany`
|
||||||
- `EAGER` for `@ManyToOne` 💀💀💀
|
- `EAGER` for `@ManyToOne` 💀💀💀
|
||||||
| `targetEntity` | The associated entity class | Determined from the attribute type declaration
|
| `targetEntity` | The associated entity class | Determined from the attribute type declaration
|
||||||
| `optional` | For `@ManyToOne` or `@OneToOne` associations, whether the association can be `null` | `true`
|
| `optional` | For a `@ManyToOne` or `@OneToOne` association, whether the association can be `null` | `true`
|
||||||
| `mappedBy` | For a bidirectional association, an attribute of the associated entity which maps the association | By default, the association is assumed unidirectional
|
| `mappedBy` | For a bidirectional association, an attribute of the associated entity which maps the association | By default, the association is assumed unidirectional
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
@ -974,6 +974,7 @@ Let's begin with the most common association multiplicity.
|
||||||
|
|
||||||
A many-to-one association is the most basic sort of association we can imagine.
|
A many-to-one association is the most basic sort of association we can imagine.
|
||||||
It maps completely naturally to a foreign key in the database.
|
It maps completely naturally to a foreign key in the database.
|
||||||
|
Almost all the associations in your domain model are going to be of this form.
|
||||||
|
|
||||||
[TIP]
|
[TIP]
|
||||||
// .One-to-many join table mappings
|
// .One-to-many join table mappings
|
||||||
|
@ -981,7 +982,7 @@ It maps completely naturally to a foreign key in the database.
|
||||||
Later, we'll see how to map a many-to-one association to an <<join-table-mappings,association table>>.
|
Later, we'll see how to map a many-to-one association to an <<join-table-mappings,association table>>.
|
||||||
====
|
====
|
||||||
|
|
||||||
The `@ManyToOne` annotation marks the "one" side of the association, and so a unidirectional many-to-one association looks like this:
|
The `@ManyToOne` annotation marks the "to one" side of the association, and so a unidirectional many-to-one association looks like this:
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
|
@ -1007,7 +1008,11 @@ The only scenario in which `fetch=EAGER` makes sense is if we think there's alwa
|
||||||
Whenever this isn't the case, remember to explicitly specify `fetch=LAZY`.
|
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`.
|
Most of the time, we would like to be able to easily navigate our associations in both directions.
|
||||||
|
We do need a way to get the `Publisher` of a given `Book`, but we would also like to be able to obtain all the ``Book``s belonging to a given publisher.
|
||||||
|
|
||||||
|
To make this association bidirectional, we need to add a collection-valued attribute to the `Publisher` class, and annotate it `@OneToMany`.
|
||||||
|
To indicate clearly that this is a bidirectional association, and to reuse any mapping information already specified in the `Book` entity, we must use the `mappedBy` annotation member to refer back to `Book.publisher`.
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
|
@ -1024,6 +1029,16 @@ class Publisher {
|
||||||
|
|
||||||
The `Publisher.books` field is called the _unowned_ side of the association.
|
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:
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
@OneToMany(mappedBy=Book_.PUBLISHER) // get used to doing it this way!
|
||||||
|
Set<Book> books;
|
||||||
|
----
|
||||||
|
We're going to use this approach for the rest of the Introduction.
|
||||||
|
|
||||||
[WARNING]
|
[WARNING]
|
||||||
// .To modify a bidirectional association, you must change the _owning side_!
|
// .To modify a bidirectional association, you must change the _owning side_!
|
||||||
====
|
====
|
||||||
|
@ -1052,7 +1067,7 @@ In particular, the `List` may not contain duplicate elements, and its order will
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
@OneToMany(mappedBy="publisher")
|
@OneToMany(mappedBy=Book_.PUBLISHER)
|
||||||
Collection<Book> books;
|
Collection<Book> books;
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -1119,7 +1134,7 @@ class Person {
|
||||||
@Id @GeneratedValue
|
@Id @GeneratedValue
|
||||||
Long id;
|
Long id;
|
||||||
|
|
||||||
@OneToOne(mappedBy = "person")
|
@OneToOne(mappedBy = Author_.PERSON)
|
||||||
Author author;
|
Author author;
|
||||||
|
|
||||||
...
|
...
|
||||||
|
@ -1142,7 +1157,7 @@ On the other hand, if _every_ `Person` was an `Author`, that is, if the associat
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
@OneToOne(optional=false, mappedBy = "person", fetch=LAZY)
|
@OneToOne(optional=false, mappedBy = Author_.PERSON, fetch=LAZY)
|
||||||
Author author;
|
Author author;
|
||||||
----
|
----
|
||||||
****
|
****
|
||||||
|
@ -1182,7 +1197,7 @@ Here, there's no extra foreign key column in the `Author` table, since the `id`
|
||||||
That is, the primary key of the `Author` table does double duty as the foreign key referring to the `Person` table.
|
That is, the primary key of the `Author` table does double duty as the foreign key referring to the `Person` table.
|
||||||
|
|
||||||
The `Person` class doesn't change.
|
The `Person` class doesn't change.
|
||||||
If the association is bidirectional, we annotate the unowned side `@OneToOne(mappedBy = "person")` just as before.
|
If the association is bidirectional, we annotate the unowned side `@OneToOne(mappedBy = Author_.PERSON)` just as before.
|
||||||
|
|
||||||
[[many-to-many]]
|
[[many-to-many]]
|
||||||
=== Many-to-many
|
=== Many-to-many
|
||||||
|
@ -1215,7 +1230,7 @@ class Book {
|
||||||
@Id @GeneratedValue
|
@Id @GeneratedValue
|
||||||
Long id;
|
Long id;
|
||||||
|
|
||||||
@ManyToMany(mappedBy="authors")
|
@ManyToMany(mappedBy=Author_.BOOKS)
|
||||||
Set<Author> authors;
|
Set<Author> authors;
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
|
@ -293,7 +293,7 @@ To set up cascading, we specify the `cascade` member of one of the association m
|
||||||
@Entity
|
@Entity
|
||||||
class Order {
|
class Order {
|
||||||
...
|
...
|
||||||
@OneToMany(mappedby="order",
|
@OneToMany(mappedby=Item_.ORDER,
|
||||||
// cascade persist(), remove(), and refresh() from Order to Item
|
// cascade persist(), remove(), and refresh() from Order to Item
|
||||||
cascade={PERSIST,REMOVE,REFRESH},
|
cascade={PERSIST,REMOVE,REFRESH},
|
||||||
// also remove() orphaned Items
|
// also remove() orphaned Items
|
||||||
|
|
|
@ -211,7 +211,7 @@ class Book { ... }
|
||||||
The `@Table` annotation can do more than just specify a name:
|
The `@Table` annotation can do more than just specify a name:
|
||||||
|
|
||||||
.`@Table` annotation members
|
.`@Table` annotation members
|
||||||
[%autowidth.stretch]
|
[cols="20,~"]
|
||||||
|===
|
|===
|
||||||
| Annotation member | Purpose
|
| Annotation member | Purpose
|
||||||
|
|
||||||
|
@ -237,7 +237,7 @@ Instead:
|
||||||
The `@SecondaryTable` annotation is even more interesting:
|
The `@SecondaryTable` annotation is even more interesting:
|
||||||
|
|
||||||
.`@SecondaryTable` annotation members
|
.`@SecondaryTable` annotation members
|
||||||
[%autowidth.stretch]
|
[cols="20,~"]
|
||||||
|===
|
|===
|
||||||
| Annotation member | Purpose
|
| Annotation member | Purpose
|
||||||
|
|
||||||
|
@ -299,7 +299,7 @@ class Author {
|
||||||
Here, there should be a `UNIQUE` constraint on _both_ columns of the association table.
|
Here, there should be a `UNIQUE` constraint on _both_ columns of the association table.
|
||||||
|
|
||||||
.`@JoinTable` annotation members
|
.`@JoinTable` annotation members
|
||||||
[%autowidth.stretch]
|
[cols="20,~"]
|
||||||
|===
|
|===
|
||||||
| Annotation member | Purpose
|
| Annotation member | Purpose
|
||||||
|
|
||||||
|
@ -341,7 +341,7 @@ We use the `@Column` annotation to map basic attributes.
|
||||||
The `@Column` annotation is not only useful for specifying the column name.
|
The `@Column` annotation is not only useful for specifying the column name.
|
||||||
|
|
||||||
.`@Column` annotation members
|
.`@Column` annotation members
|
||||||
[%autowidth.stretch]
|
[cols="20,~"]
|
||||||
|===
|
|===
|
||||||
| Annotation member | Purpose
|
| Annotation member | Purpose
|
||||||
|
|
||||||
|
@ -395,7 +395,7 @@ We don't use `@Column` to map associations.
|
||||||
The `@JoinColumn` annotation is used to customize a foreign key column.
|
The `@JoinColumn` annotation is used to customize a foreign key column.
|
||||||
|
|
||||||
.`@JoinColumn` annotation members
|
.`@JoinColumn` annotation members
|
||||||
[%autowidth.stretch]
|
[cols="20,~"]
|
||||||
|===
|
|===
|
||||||
| Annotation member | Purpose
|
| Annotation member | Purpose
|
||||||
|
|
||||||
|
@ -508,7 +508,7 @@ The `@PrimaryKeyJoinColumn` is a special-purpose annotation for mapping:
|
||||||
- the primary key column of the primary table mapped by a subclass in a `JOINED` inheritance hierarchy—which is also a foreign key referencing the primary table mapped by the root entity.
|
- the primary key column of the primary table mapped by a subclass in a `JOINED` inheritance hierarchy—which is also a foreign key referencing the primary table mapped by the root entity.
|
||||||
|
|
||||||
.`@PrimaryKeyJoinColumn` annotation members
|
.`@PrimaryKeyJoinColumn` annotation members
|
||||||
[%autowidth.stretch]
|
[cols="20,~"]
|
||||||
|===
|
|===
|
||||||
| Annotation member | Purpose
|
| Annotation member | Purpose
|
||||||
|
|
||||||
|
|
|
@ -272,7 +272,7 @@ class Publisher {
|
||||||
...
|
...
|
||||||
|
|
||||||
@Cache(usage=READ_WRITE, region="PublishedBooks")
|
@Cache(usage=READ_WRITE, region="PublishedBooks")
|
||||||
@OneToMany(mappedBy="publisher")
|
@OneToMany(mappedBy=Book_.PUBLISHER)
|
||||||
Set<Book> books;
|
Set<Book> books;
|
||||||
|
|
||||||
...
|
...
|
||||||
|
@ -363,11 +363,19 @@ This cache is utilized when the entity is retrieved using one of the operations
|
||||||
- `bySimpleNaturalId()` if just one attribute is annotation `@NaturalId`, or
|
- `bySimpleNaturalId()` if just one attribute is annotation `@NaturalId`, or
|
||||||
- `byNaturalId()` if multiple attributes are annotated `@NaturalId`.
|
- `byNaturalId()` if multiple attributes are annotated `@NaturalId`.
|
||||||
|
|
||||||
|
Here's how we can retrieve an entity by its composite natural id:
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
Book book = session.byNaturalId().using("isbn", isbn, "printing", printing).load();
|
Book book =
|
||||||
|
session.byNaturalId(Book.class)
|
||||||
|
.using(Book_.isbn, isbn)
|
||||||
|
.using(Book_.printing, printing)
|
||||||
|
.load();
|
||||||
----
|
----
|
||||||
|
|
||||||
|
Notice that this code fragment is completely typesafe.
|
||||||
|
|
||||||
[NOTE]
|
[NOTE]
|
||||||
====
|
====
|
||||||
Since the natural id cache doesn't contain the actual state of the entity, it doesn't make sense to annotate an entity `@NaturalIdCache` unless it's already eligible for storage in the second-level cache, that is, unless it's also annotated `@Cache`.
|
Since the natural id cache doesn't contain the actual state of the entity, it doesn't make sense to annotate an entity `@NaturalIdCache` unless it's already eligible for storage in the second-level cache, that is, unless it's also annotated `@Cache`.
|
||||||
|
|
Loading…
Reference in New Issue