🚀 Capítulo 19: Relacionamentos em JPA

🎯 Objetivo da Aula

Ao final desta aula, você será capaz de mapear relacionamentos entre tabelas de banco de dados utilizando as anotações @ManyToOne e @OneToMany do JPA no Spring Boot.


🏢 O Cenário Prático (Seu Desafio)

Você está mapeando a galáxia de Star Wars! O seu desafio é criar um sistema onde possamos cadastrar Planetas (como Tatooine ou Coruscant) e Personagens (como Luke Skywalker ou Obi-Wan). Como um planeta pode ter muitos personagens nascidos nele, precisamos criar um relacionamento entre essas duas entidades no banco de dados.


🧠 Fundamentos: A Teoria Traduzida

No mundo real e nos bancos de dados relacionais, as informações se relacionam.

  • @ManyToOne (Muitos para Um): Muitos personagens pertencem a um único planeta. (Ex: Luke e Anakin pertencem a Tatooine). Esta é a anotação mais comum e cria a chave estrangeira no banco.
  • @OneToMany (Um para Muitos): Um único planeta possui muitos personagens associados a ele.

No banco de dados, isso se traduz em uma Chave Estrangeira (Foreign Key) na tabela de personagens apontando para o ID da tabela de planetas.

Diagrama de Relacionamento

erDiagram
    PLANETA ||--o{ PERSONAGEM : possui
    PLANETA {
        Long id
        String nome
    }
    PERSONAGEM {
        Long id
        String nome
        Long planeta_id
    }

📖 Exemplo Guiado

Vamos criar o relacionamento Many-to-One entre Personagem e Planeta.

🛠️ Passo a Passo para Criar o Projeto no VS Code

  1. Abra o VS Code.
  2. Pressione as teclas Ctrl + Shift + P.
  3. Digite Spring Initializr: Create Maven Project e pressione Enter.
  4. Selecione a versão do Spring Boot e a linguagem Java.
  5. Group ID: com.starwars e Artifact ID: galaxia.
  6. Selecione a versão do Java instalada.
  7. Dependências: Busque e adicione Spring Web, Spring Data JPA e H2 Database. Pressione Enter.
  8. Gere o projeto e abra no VS Code.

📂 Estrutura Inicial de Pastas

galaxia/
├── src/
│   └── main/
│       ├── java/
│       │   └── com/starwars/galaxia/
│       │       └── GalaxiaApplication.java
│       └── resources/
│           └── application.properties
└── pom.xml

Siga os passos para criar as entidades relacionadas:

  1. Crie a entidade Planeta.java na pasta com/starwars/galaxia/:
package com.starwars.galaxia;
 
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
 
@Entity
public class Planeta {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String nome;
 
    public Planeta() {}
    public Planeta(String nome) { this.nome = nome; }
    
    public Long getId() { return id; }
    public String getNome() { return nome; }
}
  1. Crie a entidade Personagem.java com o relacionamento:
package com.starwars.galaxia;
 
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.JoinColumn;
 
@Entity
public class Personagem {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String nome;
 
    @ManyToOne // Muitos personagens para um planeta
    @JoinColumn(name = "planeta_id") // Nome da coluna da chave estrangeira no banco
    private Planeta planeta;
 
    public Personagem() {}
    public Personagem(String nome, Planeta planeta) { 
        this.nome = nome; 
        this.planeta = planeta; 
    }
    
    public Long getId() { return id; }
    public String getNome() { return nome; }
    public Planeta getPlaneta() { return planeta; }
}

🕹️ Como Executar e Testar no VS Code

  1. Abra o arquivo GalaxiaApplication.java.
  2. Clique em Run logo acima do método main.
  3. Como este exemplo foca no mapeamento das entidades, o principal resultado é a criação automática das tabelas no banco de dados.

Resultado Esperado: O Spring Boot iniciará com sucesso. Se você acessar o console do H2 (conforme configurado em capítulos anteriores), verá que as tabelas PLANETA e PERSONAGEM foram criadas, e a tabela PERSONAGEM possui uma coluna PLANETA_ID que serve como chave estrangeira (Foreign Key).


🛠️ Prática Obrigatória 1

Crie as interfaces PlanetaRepository e PersonagemRepository que herdam de JpaRepository. Crie um PlanetaController com um método @PostMapping para cadastrar novos planetas.


🛠️ Prática Obrigatória 2

Crie um PersonagemController com um método @PostMapping para cadastrar novos personagens. Lembre-se de que para salvar o personagem associado a um planeta, você precisará buscar o planeta pelo ID primeiro!


📤 Instruções de Entrega (GitHub Desktop + Microsoft Teams)

Neste curso, você entregará suas atividades enviando o código para o seu repositório no GitHub usando o aplicativo GitHub Desktop. Siga o passo a passo detalhado:

  1. Verifique a estrutura: Certifique-se de que sua estrutura de pastas final está idêntica à mostrada abaixo.
  2. Abra o GitHub Desktop: Certifique-se de que o repositório do seu curso está selecionado no canto superior esquerdo.
  3. Visualize as alterações: Na aba Changes (à esquerda), você verá todos os arquivos que criou ou modificou nesta aula.
  4. Faça o Commit:
    • No campo Summary (na parte inferior esquerda), digite uma mensagem curta descrevendo o que fez, ex: Finaliza atividades do Capítulo 19.
    • Clique no botão azul Commit to main (ou o nome da sua branch).
  5. Envie para a Nuvem (Push): No topo da tela, clique no botão Push origin. Isso enviará seu código do seu computador para o seu perfil no GitHub.
  6. ⚠️ IMPORTANTE (Repositório Público): Para que o professor consiga corrigir, o seu repositório no GitHub DEVE SER PÚBLICO. Repositórios privados não podem ser visualizados por quem não foi convidado.
  7. Como entregar no Microsoft Teams:
    • Copie o link do seu repositório no GitHub (ex: https://github.com/seu-usuario/seu-repositorio).
    • Abra a tarefa correspondente no Microsoft Teams.
    • Clique no botão Adicionar trabalho (ou Add work).
    • Selecione a opção Link no menu lateral.
    • Cole o link do GitHub no campo “Endereço Web” e digite um texto (ex: Meu Repositório) no campo “Texto a ser exibido”.
    • Clique em Anexar.
    • MUITO IMPORTANTE: Clique no botão Entregar (ou Turn in) no canto superior direito para concluir o envio!

📂 Estrutura Final de Pastas

galaxia/
├── src/
│   └── main/
│       ├── java/
│       │   └── com/starwars/galaxia/
│       │       ├── GalaxiaApplication.java
│       │       ├── Planeta.java
│       │       ├── Personagem.java
│       │       ├── PlanetaRepository.java
│       │       ├── PersonagemRepository.java
│       │       ├── PlanetaController.java
│       │       └── PersonagemController.java
│       └── resources/
│           └── application.properties
└── pom.xml

💡 Checkpoint de Lógica

O que você acabou de fazer é mapear a complexidade do mundo real. Quase nenhuma tabela em um sistema real vive sozinha. Clientes têm pedidos, pedidos têm itens, itens têm categorias. Saber relacionar essas tabelas no Java é o que diferencia um programador júnior de um pleno!


🔥 Desafio de Fixação (Opcional)

Pesquise como fazer o relacionamento inverso: adicione uma lista de personagens na classe Planeta usando a anotação @OneToMany(mappedBy = "planeta").


🔑 Gabarito de Código/Fórmulas

Prática 1:

public interface PlanetaRepository extends JpaRepository<Planeta, Long> {}
 
@RestController
public class PlanetaController {
    @Autowired
    private PlanetaRepository repo;
    
    @PostMapping("/planetas")
    public Planeta criar(@RequestBody Planeta p) {
        return repo.save(p);
    }
}

Prática 2:

@RestController
public class PersonagemController {
    @Autowired
    private PersonagemRepository repoPer;
    @Autowired
    private PlanetaRepository repoPla;
    
    @PostMapping("/personagens")
    public Personagem criar(@RequestBody PersonagemDTO dto) {
        // Busca o planeta pelo ID enviado no DTO
        Planeta p = repoPla.findById(dto.getPlanetaId()).get();
        Personagem per = new Personagem(dto.getNome(), p);
        return repoPer.save(per);
    }
}

Desafio:

// Na classe Planeta:
@OneToMany(mappedBy = "planeta")
private List<Personagem> personagens;

Capitulo Anterior | Proximo Capitulo