🚀 5.4 CRUD de Elite: Implementação Ponta a Ponta

As rotinas de CRUD (Create, Retrieve, Update, Delete) são o alicerce de qualquer sistema de gestão. Na Engenharia Premium, implementamos essas rotinas focando em Consistência, Validação e UX Fluida.

A Arquitetura do CRUD

O ciclo de vida de uma entidade no Green Dog (exemplo: Cliente) é orquestrado por três pilares:

  1. Controller: O cérebro que decide o fluxo.
  2. Repository: A ponte segura com o banco de dados.
  3. Templates: A interface de alta fidelidade para o usuário.

🎮 O Controller de Elite

Utilizamos Injeção de Dependência via Construtor e anotações semânticas modernas no ClienteController.

@Controller
@RequestMapping("/clientes")
@RequiredArgsConstructor
public class ClienteController {
 
    private final ClienteRepository clienteRepository;
 
    @GetMapping
    public ModelAndView list() {
        return new ModelAndView("clientes/list", "clientes", clienteRepository.findAll());
    }
 
    @GetMapping("/{id}")
    public ModelAndView view(@PathVariable("id") Cliente cliente) {
        return new ModelAndView("clientes/view", "cliente", cliente);
    }
 
    @GetMapping("/novo")
    public String createForm(@ModelAttribute Cliente cliente) {
        return "clientes/form";
    }
 
    @PostMapping
    public String save(@Valid Cliente cliente, BindingResult result, RedirectAttributes redirect) {
        if (result.hasErrors()) return "clientes/form";
        clienteRepository.save(cliente);
        redirect.addFlashAttribute("globalMessage", "Cliente processado com sucesso!");
        return "redirect:/clientes/" + cliente.getId();
    }
 
    @GetMapping("/remover/{id}")
    public String delete(@PathVariable Long id, RedirectAttributes redirect) {
        clienteRepository.deleteById(id);
        redirect.addFlashAttribute("globalMessage", "Cliente removido!");
        return "redirect:/clientes";
    }
}

🎨 Interfaces de Alta Fidelidade (Thymeleaf + Bootstrap 5)

1. Lista de Clientes (list.html)

Focada em legibilidade e ação rápida.

<section layout:fragment="content" class="container mt-4">
    <div class="d-flex justify-content-between mb-3">
        <h1>Lista de Clientes</h1>
        <a th:href="@{/clientes/novo}" class="btn btn-primary">Novo Cliente</a>
    </div>
 
    <table class="table table-hover shadow-sm">
        <thead class="table-dark">
            <tr>
                <th>ID</th>
                <th>Nome</th>
                <th>Ações</th>
            </tr>
        </thead>
        <tbody>
            <tr th:each="c : ${clientes}">
                <td th:text="${c.id}">01</td>
                <td>
                   <a th:href="@{/clientes/{id}(id=${c.id})}" th:text="${c.nome}">Nome</a>
                </td>
                <td>
                    <a th:href="@{/clientes/alterar/{id}(id=${c.id})}" class="btn btn-sm btn-outline-secondary">Editar</a>
                </td>
            </tr>
        </tbody>
    </table>
</section>

2. Formulário de Cadastro (form.html)

Implementa validações visuais em tempo real.

<section layout:fragment="content" class="container mt-4">
    <div class="card shadow">
        <div class="card-header bg-primary text-white">
            <h2 class="h5 mb-0">Cadastro de Cliente</h2>
        </div>
        <div class="card-body">
            <form th:action="@{/clientes}" th:object="${cliente}" method="post">
                <input type="hidden" th:field="*{id}">
                
                <div class="mb-3">
                    <label class="form-label">Nome Completo</label>
                    <input type="text" th:field="*{nome}" class="form-control" 
                           th:classappend="${#fields.hasErrors('nome')} ? 'is-invalid'">
                    <div class="invalid-feedback" th:errors="*{nome}">Erro</div>
                </div>
 
                <div class="mb-3">
                    <label class="form-label">Endereço de Entrega</label>
                    <textarea th:field="*{endereco}" class="form-control" rows="3"></textarea>
                </div>
 
                <div class="d-grid gap-2 d-md-flex justify-content-md-end">
                    <a th:href="@{/clientes}" class="btn btn-light me-md-2">Cancelar</a>
                    <button type="submit" class="btn btn-success px-5">Gravar</button>
                </div>
            </form>
        </div>
    </div>
</section>

🛠️ Padrões de Elite Aplicados

  • POST-Redirect-GET (PRG): Após salvar, redirecionamos o usuário. Isso evita o reenvio acidental de dados ao atualizar a página (F5).
  • Flash Attributes: Mensagens de sucesso que “sobrevivem” ao redirecionamento, desaparecendo após serem exibidas uma única vez.
  • Binding Automático: O Spring Boot converte os campos do HTML diretamente para os atributos da nossa entidade Cliente.

TIP

No Spring Boot 3.5, o suporte a Bean Validation está mais robusto do que nunca. Sinta-se livre para adicionar anotações complexas em suas entidades; o Controller as processará automaticamente via @Valid.

Agora que dominamos o CRUD básico, como lidaremos com milhares de registros? Veremos isso no capítulo de Paginação.


⬅️ Capítulo Anterior | Próximo Capítulo ➡️