Neste artigo, vamos complementar o vídeo gravado mostrando, na prática, como construir os métodos para cadastrar e atualizar uma categoria em um sistema backend utilizando Spring Boot.

Este conteúdo é especialmente pensado para estudantes que estão vendo Spring Boot pela primeira vez, explicando cada detalhe de forma simples e objetiva.


1. Salvando uma nova Categoria (POST)

CategoriaController.java

@PostMapping
public ResponseEntity<CategoriaDTO> salvarCategoria(@RequestBody CategoriaDTO categoriaDTO) {
    categoriaDTO = categoriaService.salvarCategoria(categoriaDTO);

    URI uri = ServletUriComponentsBuilder.fromCurrentRequestUri()
            .path("/{id}").buildAndExpand(categoriaDTO.getId()).toUri();

    return ResponseEntity.created(uri).body(categoriaDTO);
}

CategoriaService.java

@Transactional
public CategoriaDTO salvarCategoria(CategoriaDTO categoriaDTO) {
    Categoria categoria = new Categoria();
    categoria.setNome(categoriaDTO.getNome());
    categoria = categoriaRepository.save(categoria);

    return new CategoriaDTO(categoria);
}

Explicação do fluxo:

Passo a passo:

  1. Quando um cliente (como o Postman ou uma aplicação frontend) envia uma requisição POST com dados da categoria, o método salvarCategoria() do controller é chamado.
  2. O Spring Boot automaticamente converte o JSON recebido em um objeto CategoriaDTO.
  3. Esse DTO é enviado para a camada de serviço (CategoriaService).
  4. Na service:
    • Criamos uma nova instância da classe Categoria (não é DTO, é a entidade que representa a tabela no banco).
    • Copiamos os dados do DTO para a entidade.
    • Usamos o categoriaRepository.save(categoria) para salvar a nova categoria no banco.
  5. Depois de salvo, voltamos para o controller, construímos uma URI que aponta para o novo recurso criado, e retornamos a resposta HTTP 201 Created.

Por que usamos save() aqui?

  • Porque estamos criando um novo objeto que ainda não existe no banco.
  • O save() instrui o Hibernate a gerar o comando INSERT INTO categoria (...) VALUES (...) para armazenar os dados.

Resumo:

Se criamos um novo objeto, precisamos chamar save() para gravar no banco.


2. Atualizando uma Categoria Existente (PUT)

CategoriaController.java

@PutMapping(value = "/{id}")
public ResponseEntity<CategoriaDTO> atualizarCategoria(@PathVariable Long id, @RequestBody CategoriaDTO categoriaDTO) {
    categoriaDTO = categoriaService.atualizarCategoria(id, categoriaDTO);

    return ResponseEntity.ok().body(categoriaDTO);
}

CategoriaService.java

@Transactional
public CategoriaDTO atualizarCategoria(Long id, CategoriaDTO categoriaDTO) {
    Categoria categoria = categoriaRepository.findById(id)
            .orElseThrow(() -> new EntityNotFoundException("Id não encontrado: " + id));

    categoria.setNome(categoriaDTO.getNome());

    return new CategoriaDTO(categoria);
}

Explicação do fluxo:

Passo a passo:

  1. Quando um cliente faz uma requisição PUT para atualizar uma categoria existente, o Spring Boot captura o id da URL e o corpo do JSON como CategoriaDTO.
  2. A service tenta buscar a categoria no banco usando findById(id).
  3. Se a categoria não for encontrada, lançamos uma exceção EntityNotFoundException, que depois será tratada para devolver um erro 404.
  4. Se a categoria for encontrada:
    • Atualizamos o campo nome com o novo valor recebido.
    • Não chamamos save()! Por quê?
      • Como estamos dentro de uma transação (@Transactional), o Hibernate detecta automaticamente a alteração (chamamos isso de dirty checking) e gera o comando UPDATE no banco ao final da transação.
  5. Por fim, retornamos um CategoriaDTO atualizado para o cliente.

Por que aqui não usamos save()?

  • Quando carregamos um objeto com findById() dentro de uma transação ativa, ele passa a ser gerenciado pelo Hibernate.
  • O Hibernate monitora as mudanças e sincroniza automaticamente com o banco ao dar commit na transação.

Resumo:

Se buscamos o objeto e estamos dentro de @Transactional, o Hibernate atualiza o banco automaticamente.


3. Entendendo a expressão lambda () ->

Dentro do método de atualização, temos este trecho:

categoriaRepository.findById(id)
    .orElseThrow(() -> new EntityNotFoundException("Id não encontrado: " + id));

Aqui usamos uma expressão lambda:

  • () indica que não estamos recebendo nenhum parâmetro.
  • -> indica que o que vem depois é o que será executado.
  • new EntityNotFoundException(...) cria e retorna uma nova exceção.

Essa expressão é utilizada porque o método orElseThrow espera uma função que forneça uma exceção. Em outras palavras, estamos dizendo:

“Se o objeto não for encontrado, execute essa função que cria uma nova exceção.”

Por que usar lambda?

  • Deixa o código mais limpo e mais legível.
  • Evita ter que criar uma classe anônima ou um método separado só para isso.

Exemplo sem lambda (forma antiga):

categoriaRepository.findById(id)
    .orElseThrow(new Supplier<EntityNotFoundException>() {
        @Override
        public EntityNotFoundException get() {
            return new EntityNotFoundException("Id não encontrado: " + id);
        }
    });

Com a expressão lambda () ->, o código fica muito mais enxuto e fácil de ler.


Conclusão

  • Usamos save() para criar um novo objeto no banco de dados.
  • Atualizamos diretamente os objetos carregados dentro de uma transação sem necessidade de save() extra.
  • Sempre trabalhamos com DTOs para proteger as entidades e controlar melhor os dados que entram e saem da nossa API.
  • A separação entre Controller → Service → Repository é essencial para manter o código limpo e organizado.
  • A utilização de expressões lambda deixa o código mais moderno, claro e objetivo.

Categorized in:

Spring Boot,