Neste codelab você aprenderá a tratar erros em sua aplicação web utilizando o framework Spring Boot com Thymeleaf e acesso a dados com JdbcTemplate.

O que você vai fazer

Clonar o projeto neste repositório GitHub e adicionar tratamento de erros.

O que você vai aprender

Uma característica comum em todos programas de computador é a necessidade de tratar exceções. Em Java as exceções são tratadas por um mecanismo de lançamento de exceções quando o erro é identificado. Spring Boot oferece diversas maneiras de tratar exceções. Uma forma simples e poderosa que usaremos aqui neste tutorial é a anotação @ControllerAdvice.

Esta anotação facilita nossa vida para lidar com todos os tipos de exceções em um local central em nossa aplicação. A facilidade está em não precisar capturar exceções em cada método ou classe separadamente, bastando apenas lançar a exceção no método e, em seguida, ela será capturada na classe central de tratamento de exceções anotada por @ControllerAdvice.

Lançando exceções

Um exemplo de erro (exceção)

Divisão por zero - java.lang.ArithmeticException: / by zero

public class Teste {
  public static void main(String[] args) {
    System.out.println("chamando m1()");
    m1();
    System.out.println("fim do método main");
  }
  
  private static void m1() {
    System.out.println("chamando m2()");
    m2();
    System.out.println("fim do método m1()");
  }
  
  private static void m2() {
    System.out.println("início do m2()");
    int x = 1 / 0;
    System.out.println("fim do método m2()");
  }
}

Depois de executar o programa acima teremos o seguinte:

Como tratar este erro? Vamos modificar o código anterior para exemplificar

package com.professorangoti.condominio;

public class Teste {
  public static void main(String[] args) {
    System.out.println("chamando m1()");
    m1();
    System.out.println("fim do método main");
  }

  private static void m1() {
    System.out.println("chamando m2()");
    m2();
    System.out.println("fim do método m1()");
  }
  
  private static void m2() {
    System.out.println("início do m2()");
    try {  // colocar o código com potencial de erro dentro do bloco try
      int x = 1 / 0;
    } catch (ArithmeticException e) { //capturar as exceções que podem ocorrer
      System.out.println("Erro: " + e.getMessage());
    }
    System.out.println("fim do método m2()");
  }
}

Depois de executar o programa acima teremos o seguinte:

O bloco try/catch não deixa o programa parar, tratando o erro no local onde aconteceu. Se as exceções capturadas forem de mesma hierarquia, as classes mais especializadas devem aparecer primeiro. Para capturar qualquer exceção use a classe Exception (não indicado):

catch (Exception e) { ... }

Podemos também delegar o tratamento do erro para outro método na pilha de execução, para isso podemos lançar uma exceção .

A classe que será criada para fazer o papel de central de tratamento de exceções deverá ser anotada com a anotação @ControllerAdvice. Nesta classe vamos usar a seguinte anotação em seus métodos:

Esta anotação vai funcionar como um filtro para capturar uma exceção específica, possibilitando o tratamento do erro.

Classe MyControllerAdvice

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

@ControllerAdvice
public class MyControllerAdvice {

  @ExceptionHandler(java.sql.SQLIntegrityConstraintViolationException.class)
  public String violaçãoDeIntegridade(java.sql.SQLIntegrityConstraintViolationException ex,
      final RedirectAttributes redirectAttributes) {
    String errorMessage = ex.getMessage();
    if (errorMessage.contains("Cannot delete or update a parent row")) {
      redirectAttributes.addFlashAttribute("mensagem", "O proprietário tem apartamento cadastrado. Não é possível excluí-lo do sistema");
      return "redirect:/rel_prop";
    }
    return "error";
  }
}

Esta classe deve estar no mesmo pacote das classes controladoras (pacote controller).

Precisamos alterar o template rel_prop.html para exibir a mensagem de erro:

<!DOCTYPE html>
<html th:include="template :: modelo">
<body>
    <div th:fragment="conteudo">
        <div th:if="${mensagem}" th:text="${mensagem}" class="p-3 mb-2 bg-warning text-dark">
        </div>
        <table class="table">
            <thead>
                <tr>
                    <th scope="col">#</th>
                    <th scope="col">Nome</th>
                    <th scope="col">Telefone</th>
                    <th scope="col">Ações</th>
                </tr>
            </thead>
            <tbody>
                <tr th:each="prop : ${proprietarios}">
                    <th scope="row" th:text="${prop.id}"></th>
                    <td th:text="${prop.nome}"></td>
                    <td th:text="${prop.telefone}"></td>
                    <td>
                        <a th:href="@{/excluir_prop(id=*{prop.id})}">Excluir</a>
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
</body>
</html>