Pular para o conteúdo principal

Capítulo 5: Introdução aos Repositories Spring Data JPA

Introdução​

Agora que você domina os fundamentos do Spring Boot (estrutura, configuração, anotações), é hora de aprender a persistir dados em um banco de dados. O Spring Data JPA simplifica muito essa tarefa, permitindo que você interaja com o banco de dados sem escrever código SQL.

Objetivos do Capítulo​

Ao final deste capítulo, você será capaz de:

  • Entender o papel de um Repository Spring Data JPA
  • Utilizar os 4 métodos básicos para CRUD
  • Criar métodos de busca simples com convenções de nomenclatura
  • Compreender como o Spring gera automaticamente as queries

O que é um Repository?​

Definição​

Um Repository é uma interface que permite interagir com o banco de dados sem escrever código SQL. O Spring Data JPA gera automaticamente a implementação.

Analogia: Um repository é como um bibliotecário que gerencia seus livros (dados) para você.

Arquitetura Completa​

Responsabilidades:

CamadaPapelExemplo
ControllerGerencia as requisições HTTP@RestController
ServiceLógica de negócioUserService
RepositoryAcesso a dadosUserRepository
DatabaseArmazenamentoPostgreSQL, H2

Criando uma Entidade Simples​

Exemplo: Entidade User​

Antes de criar um repository, precisamos de uma entidade - uma classe Java que representa uma tabela no banco de dados.

package com.example.demo.model;

import jakarta.persistence.*;

@Entity
@Table(name = "users")
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private String email;
private int age;

// Construtores
public User() {
}

public User(String name, String email, int age) {
this.name = name;
this.email = email;
this.age = age;
}

// Getters e Setters
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}

Anotações Essenciais​

AnotaçãoPapelExemplo
@EntityMarca a classe como entidade JPACria uma tabela
@TableEspecifica o nome da tabelaname = "users"
@IdIdentifica a chave primáriaCampo id
@GeneratedValueGeração automática do IDAuto-incremento

Os 4 Métodos Básicos​

Interface JpaRepository​

O Spring Data JPA fornece a interface JpaRepository que já contém todos os métodos CRUD básicos.

package com.example.demo.repository;

import com.example.demo.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Não é necessário escrever código!
// Os métodos são herdados automaticamente
}

Explicação:

  • JpaRepository<User, Long>:
    • User: Tipo da entidade
    • Long: Tipo do ID

Métodos Herdados Automaticamente​

Aqui estão os 4 métodos essenciais que você pode usar imediatamente:

1. findById() - Buscar por ID​

Assinatura:

Optional<User> findById(Long id)

Uso:

@Service
public class UserService {

@Autowired
private UserRepository userRepository;

public User getUserById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found"));
}
}

SQL gerado:

SELECT * FROM users WHERE id = ?

2. findAll() - Buscar todos​

Assinatura:

List<User> findAll()

Uso:

public List<User> getAllUsers() {
return userRepository.findAll();
}

SQL gerado:

SELECT * FROM users

3. save() - Criar ou Atualizar​

Assinatura:

User save(User user)

Uso (Criação):

public User createUser(User user) {
return userRepository.save(user);
}

SQL gerado (se ID for null):

INSERT INTO users (name, email, age) VALUES (?, ?, ?)

SQL gerado (se ID existir):

UPDATE users SET name = ?, email = ?, age = ? WHERE id = ?

4. deleteById() - Remover por ID​

Assinatura:

void deleteById(Long id)

Uso:

public void deleteUser(Long id) {
userRepository.deleteById(id);
}

SQL gerado:

DELETE FROM users WHERE id = ?

Tabela Resumo​

MétodoAçãoParâmetroRetornoSQL
findById(id)Ler umIDOptional<User>SELECT ... WHERE id = ?
findAll()Ler todosNenhumList<User>SELECT *
save(user)Criar/AtualizarEntidadeUserINSERT ou UPDATE
deleteById(id)RemoverIDvoidDELETE ... WHERE id = ?

Convenções de Nomenclatura: As 3 Bases​

Princípio Geral​

O Spring Data JPA pode gerar automaticamente métodos de busca baseados no nome do método.

Formato:

findBy[Atributo]

1. findBy (Busca Simples)​

Sintaxe:

findBy + NomeAtributo

Exemplos:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

// Busca por nome
User findByName(String name);

// Busca por email
User findByEmail(String email);

// Busca por idade
List<User> findByAge(int age);
}

SQL gerado:

MétodoSQL
findByName("John")SELECT * FROM users WHERE name = 'John'
findByEmail("john@example.com")SELECT * FROM users WHERE email = 'john@example.com'
findByAge(25)SELECT * FROM users WHERE age = 25

2. And (Combinar dois critérios)​

Sintaxe:

findBy + Atributo1 + And + Atributo2

Exemplos:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

// Busca por nome E idade
List<User> findByNameAndAge(String name, int age);

// Busca por email E idade
User findByEmailAndAge(String email, int age);
}

SQL gerado:

MétodoSQL
findByNameAndAge("John", 25)SELECT * FROM users WHERE name = 'John' AND age = 25
findByEmailAndAge("john@example.com", 25)SELECT * FROM users WHERE email = 'john@example.com' AND age = 25

3. Or (Critério alternativo)​

Sintaxe:

findBy + Atributo1 + Or + Atributo2

Exemplos:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

// Busca por nome OU email
List<User> findByNameOrEmail(String name, String email);

// Busca por idade 20 OU 30
List<User> findByAgeOrAge(int age1, int age2);
}

SQL gerado:

MétodoSQL
findByNameOrEmail("John", "john@example.com")SELECT * FROM users WHERE name = 'John' OR email = 'john@example.com'

Exemplo Completo: Arquitetura em 3 Camadas​

1. Entidade (Model)​

@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private int age;

// Construtores, Getters, Setters
}

2. Repository​

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

// Métodos herdados automaticamente:
// - findById(Long id)
// - findAll()
// - save(User user)
// - deleteById(Long id)

// Métodos customizados:
User findByEmail(String email);
List<User> findByAge(int age);
List<User> findByNameAndAge(String name, int age);
}

3. Service​

@Service
public class UserService {

private final UserRepository userRepository;

public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}

// CREATE
public User createUser(User user) {
return userRepository.save(user);
}

// READ - todos
public List<User> getAllUsers() {
return userRepository.findAll();
}

// READ - por ID
public User getUserById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found with id: " + id));
}

// READ - por email
public User getUserByEmail(String email) {
return userRepository.findByEmail(email);
}

// READ - por idade
public List<User> getUsersByAge(int age) {
return userRepository.findByAge(age);
}

// UPDATE
public User updateUser(Long id, User userDetails) {
User user = getUserById(id);
user.setName(userDetails.getName());
user.setEmail(userDetails.getEmail());
user.setAge(userDetails.getAge());
return userRepository.save(user);
}

// DELETE
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}

4. Controller​

@RestController
@RequestMapping("/api/users")
public class UserController {

private final UserService userService;

public UserController(UserService userService) {
this.userService = userService;
}

// POST /api/users
@PostMapping
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}

// GET /api/users
@GetMapping
public List<User> getAllUsers() {
return userService.getAllUsers();
}

// GET /api/users/123
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}

// GET /api/users/email/john@example.com
@GetMapping("/email/{email}")
public User getUserByEmail(@PathVariable String email) {
return userService.getUserByEmail(email);
}

// GET /api/users/age/25
@GetMapping("/age/{age}")
public List<User> getUsersByAge(@PathVariable int age) {
return userService.getUsersByAge(age);
}

// PUT /api/users/123
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
return userService.updateUser(id, user);
}

// DELETE /api/users/123
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
}
}

Configuração H2 para Testes​

application.yml​

spring:
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password:

h2:
console:
enabled: true
path: /h2-console

jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
properties:
hibernate:
format_sql: true

Acesso ao console H2:

http://localhost:8080/h2-console

Exercícios Práticos​

Exercício 1: Repository Básico​

Objetivo: Criar um repository simples

Instruções:

  1. Crie uma entidade Product:
    • Long id
    • String name
    • Double price
    • int quantity
  2. Crie ProductRepository que estende JpaRepository
  3. Teste os 4 métodos básicos

Perguntas:

  • Qual método usar para criar um produto?
  • Como recuperar todos os produtos?
  • Como remover um produto pelo seu ID?

Exercício 2: Métodos de Busca​

Objetivo: Usar convenções de nomenclatura

Instruções:

  1. No ProductRepository, adicione:
    • Busca por nome
    • Busca por preço
    • Busca por nome E preço
  2. Crie um serviço ProductService
  3. Teste em um controller

Perguntas:

  • Qual SQL é gerado para findByName?
  • Como buscar produtos de um determinado preço?

Exercício 3: Serviço Completo​

Objetivo: Criar uma arquitetura completa

Instruções:

  1. Crie uma entidade Student:
    • Long id
    • String firstName
    • String lastName
    • String email
    • int grade
  2. Crie StudentRepository com:
    • Busca por email
    • Busca por grade
    • Busca por firstName e lastName
  3. Crie StudentService com todas as operações CRUD
  4. Crie StudentController com todos os endpoints

Perguntas de Revisão​

  1. O que é um Repository no Spring Data JPA?

  2. Quais são os 4 métodos herdados automaticamente do JpaRepository?

  3. Qual a diferença entre save() para criação e para atualização?

  4. Como o Spring sabe qual query SQL gerar para findByName?

  5. Qual a diferença entre And e Or nos nomes dos métodos?

  6. Por que usamos Optional<User> para findById()?

  7. O que acontece se chamarmos findById() com um ID que não existe?

  8. É possível combinar mais de 2 critérios com And? Dê um exemplo.


Resumo do Capítulo​

Pontos-Chave​

  • Um Repository é uma interface para acesso a dados
  • JpaRepository fornece 4 métodos básicos: findById, findAll, save, deleteById
  • As convenções de nomenclatura permitem criar métodos automaticamente
  • 3 palavras-chave essenciais: findBy, And, Or
  • O Spring gera o SQL automaticamente com base no nome do método
  • Arquitetura: Controller → Service → Repository → Database

O que Você Sabe Agora​

✅ Criar uma entidade JPA
✅ Criar um repository Spring Data JPA
✅ Utilizar os 4 métodos CRUD básicos
✅ Criar métodos de busca simples (findBy, And, Or)
✅ Construir uma arquitetura completa em 3 camadas

O que Você Vai Aprender Depois (Módulo 6)​

🔜 Convenções de nomenclatura avançadas (15+ palavras-chave)
🔜 Joins entre entidades
🔜 Queries JPQL customizadas
🔜 Performance e otimização
🔜 Transações


Boas Práticas​

DO (O que Fazer)​

✅ Usar injeção por construtor para os repositories
✅ Nomear os métodos conforme as convenções do Spring Data JPA
✅ Tratar os casos em que findById() não encontra nada com Optional
✅ Separar a lógica de negócio (Service) do acesso a dados (Repository)

DON'T (O que Evitar)​

❌ Colocar lógica de negócio nos repositories
❌ Chamar o repository diretamente do controller
❌ Ignorar o Optional retornado por findById()
❌ Criar nomes de métodos que não seguem as convenções


Próximos Passos​

Praticar​

Agora que você conhece os fundamentos dos repositories, é hora de praticar!

Vá para os TPs do Módulo 2:

Para ir além (próximos módulos):

Para se Aprofundar​

Interessado em queries mais avançadas? Veja:

Módulo 6: Spring Data JPA Avançado (em breve)

  • 15+ palavras-chave de busca
  • Joins entre tabelas
  • JPQL e queries customizadas
  • Otimização de performance

Voltar ao Módulo 2