# Many-to-Many and One-to-One — Hibernate / JPA

Source: https://www.skillbyai.com/en/hibernate-jpa/r-many

> Map many-to-many with a join table or an explicit entity, and one-to-one relationships.

## Join tables and shared keys

**`@ManyToMany`** maps a relationship through a **join table** (`@JoinTable` with `joinColumns` and `inverseJoinColumns`): students and courses, products and tags. Use a **`Set`** rather than a `List` for many-to-many collections, because Hibernate handles `List` bags inefficiently on removal (deleting and reinserting all rows). As soon as the relationship needs its **own attributes** (enrolment date, grade, quantity), replace `@ManyToMany` with an **explicit entity** for the association (`Enrolment`) and two `@ManyToOne`s; this is also easier to query and evolve, and many teams prefer it from the start. **`@OneToOne`** maps one-to-one relationships, such as a user and their profile. The most efficient mapping shares the primary key with **`@MapsId`**, so the child's ID equals the parent's; a separate foreign key column also works. Remember that `@OneToOne` and `@ManyToOne` default to **eager** fetching; mark them `LAZY`. A bidirectional `@OneToOne` inverse side often cannot be lazy without bytecode enhancement, a common performance surprise.

## Tags as many-to-many, enrolment as an explicit entity, profile with @MapsId

Choose the explicit entity when the link has data.

```java
@Entity
public class Article {
    @Id @GeneratedValue private Long id;

    @ManyToMany
    @JoinTable(name = "article_tags",
               joinColumns = @JoinColumn(name = "article_id"),
               inverseJoinColumns = @JoinColumn(name = "tag_id"))
    private Set<Tag> tags = new HashSet<>();      // Set, not List
}

@Entity
public class CourseEnrolment {                    // association with attributes
    @Id @GeneratedValue private Long id;
    @ManyToOne(fetch = FetchType.LAZY, optional = false) private Student student;
    @ManyToOne(fetch = FetchType.LAZY, optional = false) private Course course;
    private LocalDate enrolledOn;
    private String grade;
}

@Entity
public class UserProfile {
    @Id private Long id;                           // same value as the user's id
    @OneToOne(fetch = FetchType.LAZY)
    @MapsId
    private AppUser user;
    private String bio;
}
```

## A guest list versus a ticket stub

A plain many-to-many is a guest list linking guests to parties. Once you need to record which seat each guest had and what they paid, you need a ticket stub for each pairing: an entity of its own.

**Quiz:** A student-course relationship needs to store the grade. How should it be mapped?

- [ ] @ManyToMany with a List
- [x] An explicit association entity (e.g. Enrolment) with two @ManyToOne relationships
- [ ] A comma-separated column
- [ ] @OneToOne

*Answer:* An explicit association entity (e.g. Enrolment) with two @ManyToOne relationships. Attributes on the link require an entity for the association.
