association table mappings
This commit is contained in:
parent
6edefe6f4d
commit
3e187ad82a
|
@ -716,7 +716,7 @@ For example, `Types.VARCHAR` represents the SQL type `VARCHAR` (or `VARCHAR2` on
|
|||
Since Hibernate understand more SQL types than JDBC, there's an extended list of integer type codes in the class `org.hibernate.type.SqlTypes`.
|
||||
====
|
||||
|
||||
If a given `JavaType` does not know how to convert its instances to the type required by its partner `JdbcType`, we must help it out by providing a JPA `AttributeConverter` to perform the conversion.
|
||||
If a given `JavaType` doesn't know how to convert its instances to the type required by its partner `JdbcType`, we must help it out by providing a JPA `AttributeConverter` to perform the conversion.
|
||||
|
||||
For example, to form a basic type using `LongJavaType` and `TimestampJdbcType`, we would provide an `AttributeConverter<Long,Timestamp>`.
|
||||
|
||||
|
@ -732,7 +732,7 @@ Let's abandon our analogy right here, before we start calling this basic type a
|
|||
[[embeddable-objects]]
|
||||
=== Embeddable objects
|
||||
|
||||
An embeddable object is a Java class whose state maps to multiple columns of a table, but which does not itself have a persistent identity.
|
||||
An embeddable object is a Java class whose state maps to multiple columns of a table, but which doesn't itself have a persistent identity.
|
||||
That is, it's a class with mapped attributes, but no `@Id` attribute.
|
||||
|
||||
An embeddable object can only be made persistent by assigning it to the attribute of an entity.
|
||||
|
@ -833,6 +833,12 @@ 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.
|
||||
|
||||
[TIP]
|
||||
.One-to-many join table mappings
|
||||
====
|
||||
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:
|
||||
|
||||
[source,java]
|
||||
|
@ -847,6 +853,8 @@ class Book {
|
|||
}
|
||||
----
|
||||
|
||||
Here, the `Book` table has a foreign key column holding the identifier of the associated `Publisher`.
|
||||
|
||||
[TIP]
|
||||
.Almost all associations should be lazy
|
||||
====
|
||||
|
@ -889,7 +897,7 @@ That said, it's not a hard requirement to update the unowned side, at least if y
|
|||
.Unidirectional `@OneToMany`?
|
||||
====
|
||||
In principle Hibernate _does_ allow you to have a unidirectional one to many, that is, a `@OneToMany` with no matching `@ManyToOne` on the other side.
|
||||
In practice, this mapping is unnatural, and does not work very well.
|
||||
In practice, this mapping is unnatural, and just doesn't work very well.
|
||||
Avoid it.
|
||||
====
|
||||
|
||||
|
@ -925,6 +933,12 @@ In hindsight, we could have done more to make clear that this was always a viabl
|
|||
|
||||
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.
|
||||
|
||||
[TIP]
|
||||
.One-to-many join table mappings
|
||||
====
|
||||
Later, we'll see how to map a many-to-one association to an <<join-table-mappings,association table>>.
|
||||
====
|
||||
|
||||
A one-to-one association must be annotated `@OneToOne`:
|
||||
|
||||
[source,java]
|
||||
|
@ -941,6 +955,8 @@ class Author {
|
|||
}
|
||||
----
|
||||
|
||||
Here, the `Author` table has a foreign key column holding the identifier of the associated `Publisher`.
|
||||
|
||||
[TIP]
|
||||
.One-to-one associations are a way to represent subtyping
|
||||
====
|
||||
|
@ -1011,28 +1027,31 @@ class 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.
|
||||
Here, there's no extra foreign key column in the `Author` table, since the `id` column holds the identifier of `Person`.
|
||||
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.
|
||||
|
||||
[[many-to-many]]
|
||||
=== Many-to-many
|
||||
|
||||
A unidirectional many-to-many association is represented as a collection-valued attribute.
|
||||
It maps to a separate _association table_ in the database.
|
||||
It always maps to a separate _association table_ in the database.
|
||||
|
||||
A many-to-many association must be annotated `@ManyToMany`:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Entity
|
||||
class Book {
|
||||
@Id @GeneratedValue
|
||||
Long id;
|
||||
|
||||
|
||||
@ManyToMany
|
||||
Set<Author> authors;
|
||||
|
||||
|
||||
...
|
||||
}
|
||||
----
|
||||
|
@ -1041,6 +1060,7 @@ If the association is bidirectional, we add a very similar-looking attribute to
|
|||
|
||||
[source,java]
|
||||
----
|
||||
@Entity
|
||||
class Book {
|
||||
@Id @GeneratedValue
|
||||
Long id;
|
||||
|
@ -1130,7 +1150,7 @@ And from this point of view, SQL arrays look quite attractive, at least for cert
|
|||
If we're comfortable mapping `byte[]` to `VARBINARY(255)`, why would we shy away from mapping `DayOfWeek[]` to `TINYINT ARRAY[7]`?
|
||||
====
|
||||
|
||||
Unfortunately, JPA does not define a standard way to map SQL arrays, but here's how we can do it in Hibernate:
|
||||
Unfortunately, JPA doesn't define a standard way to map SQL arrays, but here's how we can do it in Hibernate:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
|
|
|
@ -151,7 +151,7 @@ is a bad idea, since it's impossible to create a foreign key constraint that tar
|
|||
// However, it's possible to emulate a mix of `SINGLE_TABLE` and `JOINED` inheritance using the `@SecondaryTable` annotation.
|
||||
|
||||
[[table-mappings]]
|
||||
=== Mapping entities to tables
|
||||
=== Mapping to tables
|
||||
|
||||
The following annotations specify exactly how elements of the domain model map to tables of the relational model:
|
||||
|
||||
|
@ -162,10 +162,15 @@ The following annotations specify exactly how elements of the domain model map t
|
|||
|
||||
| `@Table` | Map an entity class to its primary table
|
||||
| `@SecondaryTable` | Define a secondary table for an entity class
|
||||
| `@JoinTable` | Map a many-to-many association to its association table
|
||||
| `@JoinTable` | Map a many-to-many or many-to-one association to its association table
|
||||
| `@CollectionTable` | Map an `@ElementCollection` to its table
|
||||
|===
|
||||
|
||||
The first two annotations are used to map an entity to its _primary table_ and, optionally, one or more _secondary tables_.
|
||||
|
||||
[[entity-table-mappings]]
|
||||
=== Mapping entities to tables
|
||||
|
||||
By default, an entity maps to a single table, which may be specified using `@Table`:
|
||||
|
||||
[source,java]
|
||||
|
@ -200,12 +205,16 @@ The `@Table` annotation can do more than just specify a name:
|
|||
|===
|
||||
|
||||
[TIP]
|
||||
.Don't hardcode the schema and catalog
|
||||
====
|
||||
It's very often a bad idea to hardcode the schema and catalog in a `@Table` annotation.
|
||||
It's usually better to set the configuration properties `hibernate.default_schema` and `hibernate.default_catalog`, or simply ensure that your JDBC connection URL specifies the schema and catalog.
|
||||
.If you don't need to, don't hardcode the schema and catalog
|
||||
====
|
||||
It only makes sense to explicitly specify the `schema` in annotations if the domain model is spread across multiple schemas.
|
||||
|
||||
Otherwise, it's a bad idea to hardcode the schema (or catalog) in a `@Table` annotation.
|
||||
Instead:
|
||||
|
||||
- set the configuration property `hibernate.default_schema` (or `hibernate.default_catalog`), or
|
||||
- simply specify the schema in the JDBC connection URL.
|
||||
====
|
||||
|
||||
The `@SecondaryTable` annotation is even more interesting:
|
||||
|
||||
|
@ -223,7 +232,67 @@ The `@SecondaryTable` annotation is even more interesting:
|
|||
| `foreignKey` | An `@ForeignKey` annotation specifying the name of the `FOREIGN KEY` constraint on the ``@PrimaryKeyJoinColumn``s
|
||||
|===
|
||||
|
||||
To understand this annotation better, we must first discuss column mappings in general.
|
||||
[[join-table-mappings]]
|
||||
=== Mapping associations to tables
|
||||
|
||||
The `@JoinTable` annotation specifies an _association table_, that is, a table holding foreign keys of both associated entities.
|
||||
This annotation is usually used with `@ManyToMany` associations:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Entity
|
||||
class Book {
|
||||
...
|
||||
@ManyToMany
|
||||
@JoinTable(name="BooksAuthors")
|
||||
Set<Author> authors;
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
But it's even possible to use it to map a `@ManyToOne` or `@OneToOne` association to an association table.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Entity
|
||||
class Book {
|
||||
...
|
||||
@ManyToOne(fetch=LAZY)
|
||||
@JoinTable(name="BookPublisher")
|
||||
Publisher publisher;
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Entity
|
||||
class Author {
|
||||
...
|
||||
@OneToOne(optional=false, fetch=LAZY)
|
||||
@JoinTable(name="AuthorPerson")
|
||||
Person author;
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
.`@JoinTable` annotation members
|
||||
[cols=",8"]
|
||||
|===
|
||||
| Annotation member | Purpose
|
||||
|
||||
| `name` | The name of the mapped association table
|
||||
| `schema` 💀 | The schema to which the table belongs
|
||||
| `catalog` 💀 | The catalog to which the table belongs
|
||||
| `uniqueConstraints` | One or more `@UniqueConstraint` annotations declaring multi-column unique constraints
|
||||
| `indexes` | One or more `@Index` annotations each declaring an index
|
||||
| `joinColumns` | One or more `@JoinColumn` annotations, specifying <<join-column-mappings,foreign key column mappings>> to the table of the owning side
|
||||
| `inverseJoinColumns` | One or more `@JoinColumn` annotations, specifying <<join-column-mappings,foreign key column mappings>> to the table of the unowned side
|
||||
| `foreignKey` | An `@ForeignKey` annotation specifying the name of the `FOREIGN KEY` constraint on the ``joinColumns``s
|
||||
| `inverseForeignKey` | An `@ForeignKey` annotation specifying the name of the `FOREIGN KEY` constraint on the ``inverseJoinColumns``s
|
||||
|===
|
||||
|
||||
To better understand these annotations, we must first discuss column mappings in general.
|
||||
|
||||
[[column-mappings]]
|
||||
=== Mapping to columns
|
||||
|
@ -343,6 +412,24 @@ If you don't supply a name explicitly, Hibernate will generate a quite ugly name
|
|||
To be fair, this is perfectly fine if you're only using the generated DDL for testing.
|
||||
====
|
||||
|
||||
For associations mapped to a `@JoinTable`, fetching the association requires two joins, and so we must declare the ``@JoinColumn``s inside the `@JoinTable` annotation:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Entity
|
||||
class Book {
|
||||
@Id @GeneratedValue
|
||||
Long id;
|
||||
|
||||
@ManyToMany
|
||||
@JoinTable(joinColumns=@JoinColumn(name="bookId"),
|
||||
inverseJoinColumns=@joinColumn(name="authorId"))
|
||||
Set<Author> authors;
|
||||
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
[[primary-key-column-mappings]]
|
||||
=== Mapping primary key joins between tables
|
||||
|
||||
|
@ -457,10 +544,10 @@ Instead, as we just saw in <<column-lengths>>, all you need is to specify a larg
|
|||
[WARNING]
|
||||
.PostgreSQL `BYTEA` and `TEXT`
|
||||
====
|
||||
Unfortunately, the driver for PostgreSQL does not allow `BYTEA` or `TEXT` columns to be read via the JDBC LOB APIs.
|
||||
Unfortunately, the driver for PostgreSQL doesn't allow `BYTEA` or `TEXT` columns to be read via the JDBC LOB APIs.
|
||||
|
||||
This limitation of the Postgres driver has resulted in a whole cottage industry of bloggers and stackoverflow question-answerers recommending convoluted ways to hack the Hibernate `Dialect` for Postgres to allow an attribute annotated `@Lob` to be written using `setString()` and read using `getString()`.
|
||||
|
||||
But _simply removing the `@Lob` annotation has exactly the same effect!_
|
||||
But simply removing the `@Lob` annotation has exactly the same effect.
|
||||
====
|
||||
|
||||
|
|
Loading…
Reference in New Issue