Skip to content

Mapping Associations many to many

Steve Ebersole edited this page Feb 16, 2015 · 2 revisions

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.

Bi-directional model example
@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;
}
Bi-directional model example
@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;
}

Implicit naming rules

Bi-directional implicit model
@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)
)
Uni-directional implicit model
@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.