-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Mapping Associations many to many
A many-to-many association is a form of entity relationship in which an entity can be associated with many of another entity (collection valued, or plural) and the inverse is true as well.
It is easiest to explain this by way of an example. A book might have multiple authors and a person might have authored many books.
@Entity
@Table(name="book")
class Book {
@Id
@Column(name="id")
Integer id;
@ManyToMany
@JoinTable(
name="book_author",
joinColumns=@JoinColumn(name="book_id", referencedColumnName="id"),
inverseJoinColumns=@JoinColumn(name="author_id", referencedColumnName="id")
)
Set<Person> authors;
}
@Entity
@Table(name="person")
class Person {
@Id
@Column(name="id")
Integer id;
}
@Entity
@Table(name="book")
class Book {
@Id
@Column(name="id")
Integer id;
@ManyToMany
@JoinTable(
name="book_author",
joinColumns=@JoinColumn(name="book_id", referencedColumnName="id"),
inverseJoinColumns=@JoinColumn(name="author_id", referencedColumnName="id")
)
Set<Person> authors;
}
@Entity
@Table(name="person")
class Person {
@Id
@Column(name="id")
Integer id;
@ManyToMany(mappedBy="authors")
Set<Book> books;
}
@Entity
class Book {
@Id
Integer id;
@ManyToMany
Set<Person> authors;
}
@Entity
class Person {
@Id
Integer id;
@ManyToMany(mappedBy="authors")
Set<Book> books;
}
Here we have an implicit model in terms of the relational model to investigate some of the implicit naming rules around naming the table and columns used in the many-to-many mapping.
Firstly, the primary table for the Book
entity will be named Book
and the primary table for the Person
entity will be named Person
. The columns for the identifier column for each will be named id
. These implicit naming rules are covered elsewhere. As always, a specific org.hibernate.boot.model.naming.ImplicitNamingStrategy
can be used to alter these rules. Here we will assume the standard naming.
The first implicit name that we must determine is for the join table itself. The JPA rule for join table naming is that the primary tables for each side, owning side first, are concatenated together with an underscore (_) in between. Here that would resolve to Book_Person
.
Next we need to determine the implicit names of both the join column (@JoinTable#joinColumns
) and the inverse join column (@JoinTable#inverseJoinColumns
). Here is what the JPA spec says for the implicit naming of a join column:
The concatenation of the following: the name of the referencing relationship property or field of the referencing entity or embeddable class; ""; the name of the referenced primary key column. If there is no such referencing relationship property or field in the entity, or if the join is for an element collection, the join column name is formed as the concatenation of the following: the name of the entity; ""; the name of the referenced primary key column.
The wording there is, unfortunately, a bit opaque. The idea is that this is the property on the other side of the association. Confusingly, for a bi-directional many-to-many, there are 2 "other sides" depending on your perspective :)
JoinTable#joinColumns
describes the columns that join from the join table rows back to the Book
table. The phrase "referencing relationship property" describes the property that uses that column to reference Book`s as its elements; here, `Person#books
. Thus, the implicit name for the join column would resolve to books_id
.
@JoinTable#inverseJoinColumns
describes the columns that join the join table rows to Person
table. Here, the phrase "referencing relationship property" means the Book#authors
property. So the implicit name for the join column would resolve to authors_id
.
This gives us the following relational model:
create table Book (
id integer not null,
primary key (id)
)
create table Person (
id integer not null,
primary key (id)
)
create table Book_Person (
books_id integer not null,
authors_id integer not null,
primary key(books_id,authors_id),
foreign key (books_id) references Book(id),
foreign key (authors_id) references Person(id)
)
@Entity
class Book {
@Id
Integer id;
@ManyToMany
Set<Person> authors;
}
@Entity
class Person {
@Id
Integer id;
}
Just as we saw above with the bi-directional implicit model, the join table name here will be resolved as Book_Person
. And just as above, the implicit name of the FK column joining Book_Person
to Person
will be resolved as authors_id
.
However, the implicit name of the inverse join column will resolve differently in this uni-directional case because here we no longer have a "referencing relationship property" for that column. Specifically, the Person#books
property is not part of the model. So here we would engage the other part of the naming rule quoted from the spec and resolve to Book_id
. So all-in-all, the only difference in the relational model in these 2 cases it that Book_Person.books_id
from the bi-directional model would instead be Book_Person.Book_id
here.