diff --git a/documentation/src/main/asciidoc/introduction/Entities.adoc b/documentation/src/main/asciidoc/introduction/Entities.adoc index c82e9f1815..8272e44212 100644 --- a/documentation/src/main/asciidoc/introduction/Entities.adoc +++ b/documentation/src/main/asciidoc/introduction/Entities.adoc @@ -951,17 +951,17 @@ There are three annotations for mapping associations: `@ManyToOne`, `@OneToMany` They share some common annotation members: .Association-defining annotation members -[cols="14,~,35"] +[cols="13,~,35"] |=== | Member | Interpretation | Default value | `cascade` | Persistence operations which should <> to the associated entity; a list of ``CascadeType``s | `{}` -| `fetch` | Whether the association is eagerly <> or may be <> +| `fetch` | Whether the association is <> <> or may be <> a| - `LAZY` for `@OneToMany` and `@ManyToMany` - `EAGER` for `@ManyToOne` 💀💀💀 | `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 |=== @@ -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. 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] // .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 <>. ==== -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] ---- @@ -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`. ==== -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] ---- @@ -1024,6 +1029,16 @@ class Publisher { 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: +[source,java] +---- +@OneToMany(mappedBy=Book_.PUBLISHER) // get used to doing it this way! +Set books; +---- +We're going to use this approach for the rest of the Introduction. + [WARNING] // .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] ---- -@OneToMany(mappedBy="publisher") +@OneToMany(mappedBy=Book_.PUBLISHER) Collection books; ---- @@ -1119,7 +1134,7 @@ class Person { @Id @GeneratedValue Long id; - @OneToOne(mappedBy = "person") + @OneToOne(mappedBy = Author_.PERSON) Author author; ... @@ -1142,7 +1157,7 @@ On the other hand, if _every_ `Person` was an `Author`, that is, if the associat [source,java] ---- -@OneToOne(optional=false, mappedBy = "person", fetch=LAZY) +@OneToOne(optional=false, mappedBy = Author_.PERSON, fetch=LAZY) 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. 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 @@ -1215,7 +1230,7 @@ class Book { @Id @GeneratedValue Long id; - @ManyToMany(mappedBy="authors") + @ManyToMany(mappedBy=Author_.BOOKS) Set authors; ... diff --git a/documentation/src/main/asciidoc/introduction/Interacting.adoc b/documentation/src/main/asciidoc/introduction/Interacting.adoc index 3278e8559e..644e35e9e4 100644 --- a/documentation/src/main/asciidoc/introduction/Interacting.adoc +++ b/documentation/src/main/asciidoc/introduction/Interacting.adoc @@ -293,7 +293,7 @@ To set up cascading, we specify the `cascade` member of one of the association m @Entity class Order { ... - @OneToMany(mappedby="order", + @OneToMany(mappedby=Item_.ORDER, // cascade persist(), remove(), and refresh() from Order to Item cascade={PERSIST,REMOVE,REFRESH}, // also remove() orphaned Items diff --git a/documentation/src/main/asciidoc/introduction/Mapping.adoc b/documentation/src/main/asciidoc/introduction/Mapping.adoc index e9d9d1a9d1..dd63819789 100644 --- a/documentation/src/main/asciidoc/introduction/Mapping.adoc +++ b/documentation/src/main/asciidoc/introduction/Mapping.adoc @@ -211,7 +211,7 @@ class Book { ... } The `@Table` annotation can do more than just specify a name: .`@Table` annotation members -[%autowidth.stretch] +[cols="20,~"] |=== | Annotation member | Purpose @@ -237,7 +237,7 @@ Instead: The `@SecondaryTable` annotation is even more interesting: .`@SecondaryTable` annotation members -[%autowidth.stretch] +[cols="20,~"] |=== | Annotation member | Purpose @@ -299,7 +299,7 @@ class Author { Here, there should be a `UNIQUE` constraint on _both_ columns of the association table. .`@JoinTable` annotation members -[%autowidth.stretch] +[cols="20,~"] |=== | 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. .`@Column` annotation members -[%autowidth.stretch] +[cols="20,~"] |=== | 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. .`@JoinColumn` annotation members -[%autowidth.stretch] +[cols="20,~"] |=== | 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. .`@PrimaryKeyJoinColumn` annotation members -[%autowidth.stretch] +[cols="20,~"] |=== | Annotation member | Purpose diff --git a/documentation/src/main/asciidoc/introduction/Tuning.adoc b/documentation/src/main/asciidoc/introduction/Tuning.adoc index de3188d955..68b43e0a1f 100644 --- a/documentation/src/main/asciidoc/introduction/Tuning.adoc +++ b/documentation/src/main/asciidoc/introduction/Tuning.adoc @@ -272,7 +272,7 @@ class Publisher { ... @Cache(usage=READ_WRITE, region="PublishedBooks") - @OneToMany(mappedBy="publisher") + @OneToMany(mappedBy=Book_.PUBLISHER) Set 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 - `byNaturalId()` if multiple attributes are annotated `@NaturalId`. +Here's how we can retrieve an entity by its composite natural id: + [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] ==== 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`.