ManyToMany

Vamos aqui a um exemplo clássico (bem parecido com o que estamos fazendo).

Imagine um sistema de gerenciamento de cursos e alunos. Um aluno pode se inscrever em vários cursos e um curso pode ter vários alunos. Portanto, há uma relação Many-to-Many entre "Alunos" e "Cursos".

Nenhuma das tabelas pode ter uma coluna que referencie diretamente a outra sem criar muita redundância, pois um aluno pode estar associado a múltiplos cursos, e o curso a múltiplos alunos.

Para implementar essa relação em um banco de dados relacional, geralmente se cria uma tabela intermediária (ou tabela de junção), que mapeia a associação entre as duas tabelas principais. No exemplo acima, teríamos:

  1. Entidade Aluno

  2. Entidade Curso

  3. E faríamos o mapeamento para criar a tabela intermediária

Aluno

@Entity 
public class Aluno {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String nome;
    
    @ManyToMany
    @JoinTable(
        name = "aluno_curso", // Nome da tabela intermediária
        joinColumns = @JoinColumn(name = "aluno_id"), // Chave estrangeira de Aluno
        inverseJoinColumns = @JoinColumn(name = "curso_id") // Chave estrangeira de Curso
    )
    private Set<Curso> cursos = new HashSet<>();
    
    // Construtores, getters e setters

Curso

@Entity 
public class Curso {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String nome;
    
    @ManyToMany(mappedBy = "cursos") // Relacionamento inverso
    private Set<Aluno> alunos = new HashSet<>();
    
    // Construtores, getters e setters

Atualizado