segunda-feira, 8 de dezembro de 2025

JPA - Anotações de relacionamento e dicas úteis

        Nesse artigo vou abrodar as anotações de relacionamento do JPA trazendo algumas dicas úteis, principalmente no @ManyToMany:


 1. @OneToOne


O que é: Relacionamento um-para-um → um registro se relaciona com exatamente um outro registro, não podendo pertencer a mais de um.


- Pode ser unidirecional ou bidirecional

- Bidirecional exige 'mappedBy'.


Exemplo (unidirecional) Usuário ↔ Endereço (apenas se for 1 único endereço por pessoa)


@Entity

class Pessoa {

    @Id

    private Long id;


    @OneToOne

    private Endereco endereco;

}


Exemplo (bidirecional)


@Entity

class Pessoa {

    @Id

    private Long id;


    @OneToOne(mappedBy = "pessoa")

    private Endereco endereco;

}


@Entity

class Endereco {

    @Id

    private Long id;


    @OneToOne

    @JoinColumn(name = "pessoa_id")

    private Pessoa pessoa;

}


2. @OneToMany


O que é: Um objeto possui muitos do outro lado.

Ex.: Um cliente tem muitos pedidos.


- É bidirecional, com o lado dono sendo o '@ManyToOne'.


Exemplo (bidirecional)


@Entity

class Cliente {

    @Id

    private Long id;


    @OneToMany(mappedBy = "cliente")

    private List<Pedido> pedidos;

}


@Entity

class Pedido {

    @Id

    private Long id;


    @ManyToOne

    @JoinColumn(name = "cliente_id")

    private Cliente cliente;

}


3. @ManyToOne


O que é: Muitos objetos se relacionam com um do outro lado.


- Pode ser unidirecional, mas geralmente faz parte de um relacionamento bidirecional com '@OneToMany'.


Exemplo


@Entity

class Pedido {

    @Id

    private Long id;


    @ManyToOne

    @JoinColumn(name = "cliente_id")

    private Cliente cliente;

}


4. @ManyToMany


O que é: Muitos para muitos.

Ex.: alunos x cursos.


- Pode ser unidirecional ou bidirecional.

- Normalmente bidirecional, e JPA cria tabela intermediária.

- Pode haver casos de precisar manusear o objeto intermediário, então não use ManyToMany, modele as relações como: Na tabela intermediária -> @ManyToOne para cada lado; Nos lados -> @OneToMany para a entidade intermediária (dependendo da navegação). Vou mostrar um exemplo mais abaixo. Isso transforma o N:N em duas relações 1:N + N:1.


Exemplo @ManyToMany (bidirecional)


@Entity

class Aluno {

    @Id

    private Long id;


    @ManyToMany(mappedBy = "alunos")

    private List<Curso> cursos;

}


@Entity

class Curso {

    @Id

    private Long id;


    @ManyToMany

    @JoinTable(

        name = "aluno_curso",

        joinColumns = @JoinColumn(name = "curso_id"),

        inverseJoinColumns = @JoinColumn(name = "aluno_id")

    )

    private List<Aluno> alunos;

}


        Agora vamos ao exemplo onde precisamos trabalhar com o objeto intermediário, no caso, a tabela aluno_curso.

        Precisamos adicionar campos a ela, colocar notas dos alunos, data da matricula, etc.Passamos a ter a Entidade 

        AlunoCurso e agora tando Aluno quanto Curso apontam pra ela com @OneToMany e ela aponta pra ambos com @ManyToOne:


@Entity

public class Aluno {

    @Id

    private Long id;


    private String nome;


    @OneToMany(mappedBy = "aluno", cascade = CascadeType.ALL)

    private List<AlunoCurso> cursos = new ArrayList<>();

}


@Entity

public class Curso {


    @Id

    private Long id;


    private String nome;


    @OneToMany(mappedBy = "curso", cascade = CascadeType.ALL)

    private List<AlunoCurso> alunos = new ArrayList<>();

}


@Entity

public class AlunoCurso {


    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;


    @ManyToOne

    @JoinColumn(name = "aluno_id")

    private Aluno aluno;


    @ManyToOne

    @JoinColumn(name = "curso_id")

    private Curso curso;


    private LocalDate dataMatricula;

    private List<Double> notas;

}


Dicas importantes:

- Só faça carregamento bidirecionais se for realmente preciso.

- Sempre prefira LAZY nos @ManyToOne, evita carregar todo o curso ao carregar um aluno.

- Evite usar chave composta quando possível, ID próprio simplifica a vida.

- Os lados “Aluno” e “Curso” podem ser unidirecionais, você não é obrigado a colocar o @OneToMany neles se não precisar navegar, quem sabe usar um @Transient e carregar somente quando necessário. Por exemplo, você pode deixar o lado do Curso unidirecional, removendo o @OneToMany e colocando o @Transient apenas para uso em memória. Importante: O Curso continua relacionado à tabela intermediária (AlunoCurso) — você só está escolhendo não mapear a navegação inversa no JPA. Isso melhora performance, reduz carga de memória e evita N+1.


@Entity

public class Curso {

    @Id

    private Long id;


    private String nome;


    @Transient

    private List<AlunoCurso> alunosCurso; // carregado manualmente quando e onde precisar

}


        Se quiser pode remover completamente ou mapear apenas alunos diretamente (de forma transiente) carregando manualmente também quando precisar. Esses detalhes variam de acordo com o negócio.





Nenhum comentário:

Postar um comentário