From 7e9081b4481b35d747de4893a2c31e37a33f3e0e Mon Sep 17 00:00:00 2001 From: Gavin Date: Wed, 10 May 2023 23:16:11 +0200 Subject: [PATCH] join column mappings --- .../main/asciidoc/introduction/Mapping.adoc | 148 +++++++++++++++++- 1 file changed, 147 insertions(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/introduction/Mapping.adoc b/documentation/src/main/asciidoc/introduction/Mapping.adoc index ccecdde04c..c0e50edfee 100644 --- a/documentation/src/main/asciidoc/introduction/Mapping.adoc +++ b/documentation/src/main/asciidoc/introduction/Mapping.adoc @@ -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 to tables +=== Mapping entities to tables The following annotations specify exactly how elements of the domain model map to tables of the relational model: @@ -198,3 +198,149 @@ These annotations specify how elements of the domain model map to columns of tab | `@MapKeyColumn` | Specified a column that should be used to persist the keys of a `Map`. |=== +We use the `@Column` annotation to map basic attributes. + +[[regular-column-mappings]] +=== Mapping basic attributes to columns + +The `@Column` annotation is not only useful for specifying the column name. + +|=== +| Annotation member | Purpose + +| `name` | The name of the mapped column +| `table` | The name of the table to which this column belongs +| `length` | The length of a `VARCHAR`, `CHAR`, or `VARBINARY` column type +| `precision` | The decimal digits of precision of a `FLOAT`, `DECIMAL`, `NUMERIC`, or `TIME`, or `TIMESTAMP` column type +| `scale` | The scale of a `DECIMAL` or `NUMERIC` column type, the digits of precision that occur to the right of the decimal point +| `unique` | Whether the column has a `UNIQUE` constraint +| `nullable` | Whether the column has a `NOT NULL` constraint +| `insertable` | Whether the column should appear in generated SQL `INSERT` and `UPDATE` statements +| `updatable` | Whether the column should appear in generated SQL `INSERT` and `UPDATE` statements +| `columnDefinition` 💀| A DDL fragment that should be used to declare the column +|=== + +[TIP] +.Use of `columnDefinition` results in unportable DDL +==== +We no longer recommend the use of `columnDefinition`. +Hibernate has much better ways to customize the generated DDL using techniques that result in portable behavior across different databases. +==== + +Here we see four different ways to use the `@Column` annotation: + +[source,java] +---- +@Entity +@Table(name="Books") +@SecondaryTable(name="Editions") +class Book { + @Id @GeneratedValue + @Column(name="bookId") // customize column name + Long id; + + @Column(length=100, nullable=false) // declare column as VARCHAR(100) NOT NULL + String title; + + @Column(length=17, unique=true, nullable=false) // declare column as VARCHAR(17) NOT NULL UNIQUE + String isbn; + + @Column(table="Editions", updatable=false) // column belongs to the secondary table, and is never updated + int edition; +} +---- + +We don't use `@Column` to map associations. + +[[join-column-mappings]] +=== Mapping associations to foreign key columns + +The `@JoinColumn` annotation is used to customize a foreign key column. + +|=== +| Annotation member | Purpose + +| `name` | The name of the mapped foreign key column +| `table` | The name of the table to which this column belongs +| `referencedColumnName` | The name of the column to which the mapped foreign key column refers +| `unique` | Whether the column has a `UNIQUE` constraint +| `nullable` | Whether the column has a `NOT NULL` constraint +| `insertable` | Whether the column should appear in generated SQL `INSERT` and `UPDATE` statements +| `updatable` | Whether the column should appear in generated SQL `INSERT` and `UPDATE` statements +| `columnDefinition` 💀| A DDL fragment that should be used to declare the column +| `foreignKey` | A `@ForeignKey` annotation specifying the name of the `FOREIGN KEY` constraint +|=== + +Here we see how to use `@JoinColumn` to define a `@ManyToOne` association which mapping a foreign key column which refers to a non-primary-key `UNIQUE` column of the referenced table. + +[source,java] +---- +@Entity +@Table(name="Items") +class Item { + ... + @ManyToOne(optional=false) // implies nullable=false + @JoinColumn(name = "bookIsbn", referencedColumnName = "isbn", // a reference to a non-PK column + foreignKey = @ForeignKey(name="ItemsToBooksBySsn")) // supply a name for the FOREIGN KEY constraint + Book book; + ... +} +---- + +[TIP] +.Foreign key constraint names +==== +Notice the use of `@ForeignKey` to customize the name of the foreign key constraint. +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. +==== + +[[primary-key-column-mappings]] +=== Mapping primary key joins between tables + +The `@PrimaryKeyJoinColumn` is a special-purpose annotation for mapping: + +- the primary key column of a `@SecondaryTable`—which is also a foreign key referencing the primary table, or +- 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. + +|=== +| Annotation member | Purpose + +| `name` | The name of the mapped foreign key column +| `referencedColumnName` | The name of the column to which the mapped foreign key column refers +| `columnDefinition` 💀| A DDL fragment that should be used to declare the column +| `foreignKey` | A `@ForeignKey` annotation specifying the name of the `FOREIGN KEY` constraint +|=== + +When mapping a subclass table primary key, we place the `@PrimaryKeyJoinColumn` annotation on the entity class: + +[source,java] +---- +@Entity +@Table(name="People") +@Inheritance(strategy=JOINED) +class Person { ... } + +@Entity +@Table(name="Authors") +@PrimaryKeyJoinColumn(name="personId") // the name of the primary key of the Authors table +class Author { ... } +---- + +But to map a secondary table primary key, the `@PrimaryKeyJoinColumn` annotation must occur inside the `@SecondaryTable` annotation: + +[source,java] +---- +@Entity +@Table(name="Books") +@SecondaryTable(name="Editions", + pkJoinColumns = @PrimaryKeyJoinColumn(name="bookId")) // the name of the primary key of the Editions table +class Book { + @Id @GeneratedValue + @Column(name="bookId") // the name of the primary key of the Books table + Long id; + + ... +} +---- +