Prática
Aplicação Funcionários WEB com Thymeleaf e MySQL
Arquitetura Spring Boot

Passo a passo
Last updated
Aplicação Funcionários WEB com Thymeleaf e MySQL

Last updated
src/
main/
java/
com/
barbieri/
employeedbweb/
controller/
repository/
service/
entity/
resources/
test/package com.barbieri.employeebdweb.entity;
import java.util.Objects;
public class Employee {
private long id;
private String name;
private int age;
private String position;
private String department;
private String city;
private String state;
private String workFormat;
private double salary;
public Employee(long id, String name, int age, String position, String department, String city, String state,
String workFormat, double salary) {
super();
this.id = id;
this.name = name;
this.age = age;
this.position = position;
this.department = department;
this.city = city;
this.state = state;
this.workFormat = workFormat;
this.salary = salary;
}
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 int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getWorkFormat() {
return workFormat;
}
public void setWorkFormat(String workFormat) {
this.workFormat = workFormat;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Employee [id= " + id + ", name=" + name + ", age=" + age + ", position=" + position + ", department=" + department
+ ", city=" + city + ", state=" + state + ", workFormat=" + workFormat + ", salary=" + salary
+ ", getId()=" + getId() + ", getName()=" + getName() + ", getAge()=" + getAge() + ", getPosition()=" + getPosition()
+ ", getDepartment()=" + getDepartment() + ", getCity()=" + getCity() + ", getState()=" + getState()
+ ", getWorkFormat()=" + getWorkFormat() + ", getSalary()=" + getSalary() + ", getClass()=" + getClass()
+ ", hashCode()=" + hashCode() + ", toString()=" + super.toString() + "]";
}
@Override
public int hashCode() {
return Objects.hash(id, age, city, department, name, position, salary, state, workFormat);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
return id == other.id && age == other.age && Objects.equals(city, other.city) && Objects.equals(department, other.department)
&& Objects.equals(name, other.name) && Objects.equals(position, other.position)
&& Double.doubleToLongBits(salary) == Double.doubleToLongBits(other.salary)
&& Objects.equals(state, other.state) && Objects.equals(workFormat, other.workFormat);
}
}package com.barbieri.employeebdweb.service;
import java.util.List;
import java.util.Optional;
import com.barbieri.employeebdweb.entity.Employee;
public interface EmployeeService {
List<Employee> listAll();
Optional<Employee> listById(long id);
void save(Employee employee);
void update(Employee employee);
void delete(long id);
}package com.barbieri.employeebdweb.service;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.barbieri.employeebdweb.entity.Employee;
import com.barbieri.employeebdweb.repository.EmployeeRepository;
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@Override
public List<Employee> listAll() {
List<Employee> employees = new ArrayList<>();
employeeRepository.findAll().forEach(employees::add);
return employees;
}
@Override
public Optional<Employee> listById(long id) {
return employeeRepository.findById(id);
}
@Override
public void save(Employee employee) {
employeeRepository.save(employee);
}
@Override
public void update(Employee employee) {
Optional<Employee> employeeFound =
employeeRepository.findById(employee.getId());
employeeFound.ifPresent(
p -> {
employeeRepository.save(employee);
}
);
}
@Override
public void delete(long id) {
Optional<Employee> employeeFound = employeeRepository.findById(id);
employeeFound.ifPresent(
p -> {
employeeRepository.delete(employeeFound.get());
}
);
}
}
package com.barbieri.employeebdweb.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.barbieri.employeebdweb.entity.Employee;
@Repository
public interface EmployeeRepository extends CrudRepository<Employee,Long> {
}package com.barbieri.employeebdweb.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import com.barbieri.employeebdweb.service.EmployeeService;
public class EmployeeController {
private EmployeeService employeeService;
@Autowired
public void setEmployeeService(EmployeeService employeeService)
{
this.employeeService = employeeService;
}
@GetMapping("/listEmployees")
public String listEmployees(Model model)
{
model.addAttribute("employees",employeeService.listAll());
return "employees";
}
@GetMapping("/listEmployee/{id}")
public String listEmployees(@PathVariable long id, Model model)
{
model.addAttribute("employee",employeeService.listById(id));
return "employees";
}
}
package com.barbieri.employeebdweb.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class Error implements ErrorController {
@RequestMapping("/error")
public String handleError(HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute("jakarta.servlet.error.status_code");
if (statusCode != null) {
if (statusCode == 404) {
return "error/404"; // Thymeleaf carrega templates/error/404.html
} else if (statusCode == 500) {
return "error/500";
}
}
return "error/default"; // caso queira tratar outros erros
}
}
spring.application.name=employeebdweb
# Mysql Database
spring.datasource.url=jdbc:mysql://localhost:3306/employee
spring.datasource.username=root
spring.datasource.password=123456
# ThymeLeaf
spring.thymeleaf.template-loader-path: classpath:/templates
spring.thymeleaf.suffix: .html
spring.thymeleaf.cache: falsesrc/main/resources/templates/
├── index.html
├── employees.html
├── employee.html
├── editemployee.html
├── newemployee.html
├── fragments/
│ └── navbar.html
└── error/
├── mensagem.html
├── 400.html
├── 404.hml
└── 500.html
src/main/resources/static/images/
└── image.png<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Início</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Adicionando custom.css -->
<link th:href="@{/css/custom.css}" rel="stylesheet">
</head>
<body>
<div th:insert="("~{fragments/navbar :: navbar}")"></div>
<div class="container mt-5">
<h1 class="text-center">Bem-vindo ao Sistema de Funcionários</h1>
<p class="text-center">Gerencie seus funcionários de forma simples e rápida.</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Funcionários</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div th:replace="~{fragments/navbar :: navbar}"></div>
<div class="container mt-5">
<h2>Lista de Funcionários</h2>
<table class="table table-striped table-bordered">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Nome</th>
<th>Idade</th>
<th>Cargo</th>
<th>Departamento</th>
<th>Cidade</th>
<th>Estado</th>
<th>Formato</th>
<th>Salário</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
<tr th:each="employee : ${employees}">
<td th:text="${employee.id}"></td>
<td th:text="${employee.name}"></td>
<td th:text="${employee.age}"></td>
<td th:text="${employee.position}"></td>
<td th:text="${employee.department}"></td>
<td th:text="${employee.city}"></td>
<td th:text="${employee.state}"></td>
<td th:text="${employee.workFormat}"></td>
<td th:text="${employee.salary}"></td>
<td>
<a th:href="@{/employees/list/{id}(id=${employee.id})}" class="btn btn-info btn-sm">Ver</a>
<a th:href="@{/employees/edit/{id}(id=${employee.id})}" class="btn btn-warning btn-sm">Editar</a>
<a th:href="@{/employees/delete/{id}(id=${employee.id})}" class="btn btn-danger btn-sm">Excluir</a>
</td>
</tr>
</tbody>
</table>
<a th:href="@{/employees/new}" class="btn btn-success">Novo Funcionário</a>
</div>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Detalhes do Funcionário</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div th:replace="~{fragments/navbar :: navbar}"></div>
<div class="container mt-5">
<h2>Detalhes do Funcionário</h2>
<form th:object="${employee}" method="post">
<div class="col-md-6">
<label class="form-label">Nome</label>
<input type="text" class="form-control" th:field="*{name}">
</div>
<div class="col-md-2">
<label class="form-label">Idade</label>
<input type="number" class="form-control" th:field="*{age}">
</div>
<div class="col-md-4">
<label class="form-label">Cargo</label>
<input type="text" class="form-control" th:field="*{position}">
</div>
<div class="col-md-6">
<label class="form-label">Departamento</label>
<input type="text" class="form-control" th:field="*{department}">
</div>
<div class="col-md-4">
<label class="form-label">Cidade</label>
<input type="text" class="form-control" th:field="*{city}">
</div>
<div class="col-md-2">
<label class="form-label">Estado</label>
<input type="text" class="form-control" th:field="*{state}">
</div>
<div class="col-md-4">
<label class="form-label">Formato</label>
<input type="text" class="form-control" th:field="*{workFormat}">
</div>
<div class="col-md-4">
<label class="form-label">Salário</label>
<input type="number" class="form-control" step="0.01" th:field="*{salary}">
</div>
<div class="col-12">
<button type="submit" class="btn btn-primary">Salvar</button>
<a th:href="@{/employees}" class="btn btn-secondary">Cancelar</a>
</div>
</form>
</div>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Editar Funcionário</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div th:replace="~{fragments/navbar :: navbar}"></div>
<div class="container mt-5">
<h2>Editar Funcionário</h2>
<form th:action="@{'/employees/update/' + ${employee.id}}" th:object="${employee}" method="post">
<div class="col-md-6">
<label class="form-label">Nome</label>
<input type="text" class="form-control" th:field="*{name}">
</div>
<div class="col-md-2">
<label class="form-label">Idade</label>
<input type="number" class="form-control" th:field="*{age}">
</div>
<div class="col-md-4">
<label class="form-label">Cargo</label>
<input type="text" class="form-control" th:field="*{position}">
</div>
<div class="col-md-6">
<label class="form-label">Departamento</label>
<input type="text" class="form-control" th:field="*{department}">
</div>
<div class="col-md-4">
<label class="form-label">Cidade</label>
<input type="text" class="form-control" th:field="*{city}">
</div>
<div class="col-md-2">
<label class="form-label">Estado</label>
<input type="text" class="form-control" th:field="*{state}">
</div>
<div class="col-md-4">
<label class="form-label">Formato</label>
<input type="text" class="form-control" th:field="*{workFormat}">
</div>
<div class="col-md-4">
<label class="form-label">Salário</label>
<input type="number" class="form-control" step="0.01" th:field="*{salary}">
</div>
<div class="col-12">
<button type="submit" class="btn btn-primary">Salvar</button>
<a th:href="@{/employees}" class="btn btn-secondary">Cancelar</a>
</div>
</form>
</div>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Novo Funcionário</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div th:replace="~{fragments/navbar :: navbar}"></div>
<div class="container mt-5">
<h2>Novo Funcionário</h2>
<form th:action="@{/employees}" th:object="${employee}" method="post" class="row g-3">
<div class="col-md-6">
<label class="form-label">Nome</label>
<input type="text" class="form-control" th:field="*{name}">
</div>
<div class="col-md-2">
<label class="form-label">Idade</label>
<input type="number" class="form-control" th:field="*{age}">
</div>
<div class="col-md-4">
<label class="form-label">Cargo</label>
<input type="text" class="form-control" th:field="*{position}">
</div>
<div class="col-md-6">
<label class="form-label">Departamento</label>
<input type="text" class="form-control" th:field="*{department}">
</div>
<div class="col-md-4">
<label class="form-label">Cidade</label>
<input type="text" class="form-control" th:field="*{city}">
</div>
<div class="col-md-2">
<label class="form-label">Estado</label>
<input type="text" class="form-control" th:field="*{state}">
</div>
<div class="col-md-4">
<label class="form-label">Formato</label>
<input type="text" class="form-control" th:field="*{workFormat}">
</div>
<div class="col-md-4">
<label class="form-label">Salário</label>
<input type="number" class="form-control" step="0.01" th:field="*{salary}">
</div>
<div class="col-12">
<button type="submit" class="btn btn-primary">Salvar</button>
<a th:href="@{/employees}" class="btn btn-secondary">Cancelar</a>
</div>
</form>
</div>
</body>
</html>
<div th:fragment="navbar">
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<!-- Logo -->
<!-- <a class="navbar-brand" th:href="@{/}">
<img th:src="@{/images/image.png}" alt="Logo" height="40" class="d-inline-block align-text-top me-2">
Employees
</a> -->
<!-- Botão para menu mobile -->
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<!-- Itens do menu -->
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" th:href="@{/}" th:classappend="${#httpServletRequest.requestURI == '/'} ? 'active'">
Início
</a>
</li>
<li class="nav-item">
<a class="nav-link" th:href="@{/listEmployees}" th:classappend="${#httpServletRequest.requestURI.startsWith('/employees') && !#httpServletRequest.requestURI.contains('/new')} ? 'active'">
Funcionários
</a>
</li>
<li class="nav-item">
<a class="nav-link" th:href="@{/employees/new}" th:classappend="${#httpServletRequest.requestURI.contains('/employees/new')} ? 'active'">
Novo Funcionário
</a>
</li>
</ul>
</div>
</div>
</nav>
</div>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${mensagemTitulo}">Mensagem do Sistema</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<!-- Navbar comum -->
<div th:replace="~{fragments/navbar :: navbar}"></div>
<div class="container mt-5 d-flex justify-content-center">
<div class="card shadow-lg" style="max-width: 600px; width: 100%;">
<div th:class="'card-header text-white ' + ${mensagemTipo} + ' text-center'">
<h2 class="mb-0" th:text="${mensagemTitulo}">Título da Mensagem</h2>
</div>
<div class="card-body text-center">
<p th:text="${mensagemCorpo}" class="lead">Corpo da mensagem</p>
<a th:href="@{/}" class="btn btn-primary mt-3">Voltar para a página inicial</a>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Erro 400</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div th:replace="~{fragments/navbar :: navbar}"></div>
<div class="container text-center mt-5">
<h1 class="display-3 text-danger">400</h1>
<p class="lead">Requisição inválida.</p>
<a th:href="@{/}" class="btn btn-primary">Voltar para a página inicial</a>
</div>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Erro 404</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div th:replace="~{fragments/navbar :: navbar}"></div>
<div class="container text-center mt-5">
<h1 class="display-3 text-warning">404</h1>
<p class="lead">A página que você procura não foi encontrada.</p>
<a th:href="@{/}" class="btn btn-primary">Voltar para a página inicial</a>
</div>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${mensagemTitulo}">Mensagem do Sistema</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<!-- Navbar comum -->
<div th:replace="~{fragments/navbar :: navbar}"></div>
<div class="container mt-5 d-flex justify-content-center">
<div class="card shadow-lg" style="max-width: 600px; width: 100%;">
<div th:class="'card-header text-white ' + ${mensagemTipo} + ' text-center'">
<h2 class="mb-0" th:text="${mensagemTitulo}">Título da Mensagem</h2>
</div>
<div class="card-body text-center">
<p th:text="${mensagemCorpo}" class="lead">Corpo da mensagem</p>
<a th:href="@{/}" class="btn btn-primary mt-3">Voltar para a página inicial</a>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>