Skip to content
thesarfo

Concept

Persistence: Many-to-Many Relationships

Modeling a JPA many-to-many relationship through a join table, with @JoinTable and mappedBy.

views 0

Since a many-to-many relationship has “many” on both sides, it can’t be represented with a single foreign key. Instead we use a join table — a table that doesn’t map to an entity itself, used behind the scenes to represent the relationship defined through your objects.

The join table normally contains the primary keys of both entities, and is usually named with the convention entity1_entity2.

As with one-to-one relationships, you can specify the owner with @JoinColumn, though it matters less here since Hibernate doesn’t generate anything extra behind the scenes either way. And just like the other relationship types, you can define this unidirectionally or bidirectionally.

@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
// getters and setters
}
@Entity
public class Group {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private List<User> users;
}

A unidirectional relationship from Group to User (the user doesn’t know about the group): @JoinColumn provides the primary key of the current entity (the foreign key for the join table), and inverseJoinColumns provides the inverse — from the other entity in the relationship. The entity carrying @JoinTable becomes the owner of the relationship.

// in the group entity
@ManyToMany
@JoinTable(
name = "users_groups",
joinColumns = @JoinColumn(name = "group_id"),
inverseJoinColumns = @JoinColumn(name = "user_id")
)
private List<User> users;

Without cascading, you’ll have to persist instances of each entity manually.

For a bidirectional relationship, add mappedBy on the non-owning side:

// in the user entity
@ManyToMany(mappedBy = "users")
private List<Group> groups;

This is a bit tricky in practice — you now need to set groups when creating a user, and users when creating a group, but in exchange you can write queries from either side of the relationship.

Note that by default, fetch types for many-to-many relationships are LAZY, since they’re collections.