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