JPA Cheatsheet: visualizando relacionamentos
Tags: java, jpa, hibernate, springboot
Published: 2023-08-25 14:59:41

Modelar e descrever relações entre entidades pode ser uma dificuldade para desenvolvedores iniciantes, mas no caso do JPA pode virar uma completa bagunça se não prestar a devida atenção. Quando aprendi esta tecnologia criei um pequeno guia pra me ajudar e me baseio nele até hoje quando estou com alguma dúvida.

## OneToMany ou ManyToOne relationship

Regra: geralmente, o lado “many” é o dono da relação.

Exemplo: `Cliente` e `Pedido`.

Perguntas a se fazer:

- Um cliente pode ter quantos pedidos?
- Um pedido pode pertencer a quantos clientes?
- Eu preciso saber quais os pedidos do cliente?
- Eu preciso saber qual o cliente de cada pedido?
- Quem é o dono do relacionamento?

Respostas:

- [x]  Um cliente pode ter vários pedidos. (OneToMany)
- [x]  Um pedido pode pertencer a apenas um cliente. (ManyToOne)
- [x]  Eu não preciso saber os pedidos de cada cliente, mas preciso saber o cliente de cada pedido. (Unidirecional)
- [x]  O pedido é o dono do relacionamento.

Portanto, chegamos a conclusão de que um cliente pode ter vários pedidos, mas um pedido pertence a apenas um cliente.

Na classe pedido, nós declaramos o atributo cliente com as anotações:

- `@ManyToOne` (pois vários pedidos (Many) podem ter apenas um cliente (ToOne)
- `@JoinColumn(name = “cliente_id”)` para criar uma coluna no banco de dados com o id do cliente

Classe Pedido:

```java
@ManyToOne
@JoinColumn(name = “cliente_id”)
private Cliente cliente;
```

Na classe cliente, nós declaramos e INSTANCIAMOS uma coleção de pedidos com as anotações:

- `@JsonIgnore` (para a resposta em json da requisição HTTP ignorar esta lista e não criar um loop de pedidos com cliente e cliente com pedidos, gerando stack overflow, afinal nós não precisamos saber os itens). Uma alternativa ao JsonIgnore pode ser usar DTO’s.

- `@OneToMany(mappedBy = “cliente”)` pois um cliente (One) pode ter vários pedidos (ToMany), o parâmetro mappedBy serve para mapear o atributo da classe Pedido (deve ser exatamente igual ao atributo declarado na classe)

Classe Cliente:

```java
@OneToMany(mappedBy = “cliente”)
private List<Pedido> pedidos;
```

---

## ManyToMany relationship

Exemplo: Um produto pode pertencer a várias categorias, e uma categoria possui vários produtos.

Perguntas a se fazer:

- Um produto pode pertencer a quantas categorias?
- Uma categoria pode possuir quantos produtos?
- Eu preciso saber quais produtos uma categoria possui?
- Eu preciso saber a quais categorias um produto pertence?
- Quem é o dono do relacionamento?

Respostas:

- [x]  Um produto pode pertencer a varias categorias. (ManyToMany)
- [x]  Uma categoria pode possuir vários produtos. (ManyToMany)
- [x]  Eu preciso saber quais produtos uma categoria possui, e também saber a quais categorias um produto pertence. (Bidirecional)
- [x]  O produto é o dono do relacionamento.

Portanto, chegamos a conclusão de que um produto pode pertencer a várias categorias, e uma categoria pode possuir vários produtos. Nesse caso, devemos criar uma tabela de associação usando o @JoinTable. Como o Produto possui essa anotação, é implícito que o Produto é o dono da relação.

Na classe produto, declaramos e INSTANCIAMOS uma coleção de categorias com as anotações:

- `@ManyToMany` (pois vários produtos (Many) podem ser de várias categorias (ToMany)
- `@JoinTable` (para criar uma nova tabela)
- `@JoinColumn` (dentro do @JoinTable para criar as colunas)

Classe Produto:

```java
@ManyToMany
@JoinTable(name = "tb_product_category",
joinColumns = @JoinColumn(name = "product_id"),
inverseJoinColumns = @JoinColumn(name = "category_id"))
private Set<Category> categories = new HashSet<>();
```

Na classe category, declaramos e INSTANCIAMOS uma coleção de produtos com as anotações:

- `@ManyToMany(mappedBy = “categories”)` para mapear a coleção de categorias da classe Produto e indicar que o dono do relacionamento é o Produto.

Classe Categoria:

```java
@ManyToMany(mappedBy = "categories")
private Set<Product> products = new HashSet<>();
```

---

## OneToOne relationship

Exemplo: Um usuário pode ter um perfil associado, e um perfil pertence a apenas um usuário.

Perguntas a se fazer:

- Um usuário pode ter quantos perfis?
- Um perfil pode pertencer a quantos usuários?
- Eu preciso saber o perfil de cada usuário?
- Eu preciso saber o usuário associado a cada perfil?
- Quem é o dono do relacionamento?

Respostas:

- [x]  Um usuário pode ter apenas um perfil. (OneToOne)
- [x]  Um perfil pode pertencer a apenas um usuário. (OneToOne)
- [x]  Eu preciso saber o perfil de cada usuário, e também o usuário associado a cada perfil. (Bidirecional)
- [x]  O usuário é o dono do relacionamento.

Portanto, concluímos que um usuário pode ter apenas um perfil, e um perfil pertence a apenas um usuário. Nesse caso, o usuário será o dono do relacionamento.

Na classe **`Usuario`**, declaramos o atributo **`perfil`** com as anotações:

```java
@OneToOne(mappedBy = "usuario")
private Perfil perfil;
```

Na classe **`Perfil`**, declaramos o atributo **`usuario`** com as anotações:

```java
@OneToOne
@JoinColumn(name = "usuario_id")
private Usuario usuario;
```

Observe que a anotação **`@OneToOne`** é utilizada em ambas as classes para indicar a relação One-To-One. A classe **`Perfil`** possui a anotação **`@JoinColumn`** para criar uma coluna no banco de dados com o **`id`** do usuário, que será usado para mapear o relacionamento. A classe **`Usuario`** utiliza a anotação **`mappedBy`** no atributo **`perfil`** para indicar que o relacionamento é mapeado pelo atributo **`usuario`** na classe **`Perfil`**.

Dessa forma, temos uma relação One-To-One entre **`Usuario`** e **`Perfil`**, em que um usuário pode ter apenas um perfil e um perfil pertence a apenas um usuário.