💻 Exemplos: Capítulo 20: Projeto Integrador 2: Aplicação Desktop Corporativa Completa (JavaFX + JDBC + MVC + DAO)
Especialização em Sistemas com Java 21 LTS • Capítulo 20
🎯 Visão Geral dos Exemplos Práticos
Neste laboratório prático, implementaremos exemplos corporativos completos explorando:
- Arquitetura Multicamadas Completa (MVC + Service + DAO + JDBC): fluxo end-to-end integrando persistência em banco relacional e regras de negócio.
- Prevenção de UI Freezing com Tarefas Assíncronas: carregamento de dados em segundo plano utilizando
javafx.concurrent.Task. - Mapeamento Relacional e Consultas com JOIN: transformação de cursores
ResultSetcomplexos em grafos de objetos orientados a objetos.
💻 Exemplo 01 — Sistema Corporativo Integrado de Gestão Funcional
package com.empresa.projeto;
import java.math.BigDecimal;
import java.sql.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
* Projeto Integrador Corporativo: Sistema completo multicamadas com DAO, JDBC e regras de negócio.
*/
public class SistemaGestaoCompletoApp {
public record Departamento(int id, String sigla, String nome) {}
public record Funcionario(int id, String nome, String email, BigDecimal salario, LocalDate admissao, Departamento dep) {}
// Camada DAO
public interface FuncionarioDao {
void cadastrar(Funcionario f, Connection conn) throws SQLException;
List<Funcionario> listarComDepartamento(Connection conn) throws SQLException;
BigDecimal calcularFolhaPorDepartamento(int depId, Connection conn) throws SQLException;
}
public static class FuncionarioDaoJdbc implements FuncionarioDao {
@Override
public void cadastrar(Funcionario f, Connection conn) throws SQLException {
String sql = "INSERT INTO funcionarios (nome, email, salario, data_admissao, departamento_id) VALUES (?, ?, ?, ?, ?)";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, f.nome());
ps.setString(2, f.email());
ps.setBigDecimal(3, f.salario());
ps.setDate(4, java.sql.Date.valueOf(f.admissao()));
ps.setInt(5, f.dep().id());
ps.executeUpdate();
}
}
@Override
public List<Funcionario> listarComDepartamento(Connection conn) throws SQLException {
String sql = """
SELECT f.id, f.nome, f.email, f.salario, f.data_admissao,
d.id AS dep_id, d.sigla AS dep_sigla, d.nome AS dep_nome
FROM funcionarios f
INNER JOIN departamentos d ON f.departamento_id = d.id
ORDER BY d.sigla, f.nome
""";
List<Funcionario> resultado = new ArrayList<>();
try (Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql)) {
while (rs.next()) {
Departamento dep = new Departamento(rs.getInt("dep_id"), rs.getString("dep_sigla"), rs.getString("dep_nome"));
resultado.add(new Funcionario(
rs.getInt("id"),
rs.getString("nome"),
rs.getString("email"),
rs.getBigDecimal("salario"),
rs.getDate("data_admissao").toLocalDate(),
dep
));
}
}
return resultado;
}
@Override
public BigDecimal calcularFolhaPorDepartamento(int depId, Connection conn) throws SQLException {
String sql = "SELECT SUM(salario) as total FROM funcionarios WHERE departamento_id = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, depId);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next() && rs.getBigDecimal("total") != null) {
return rs.getBigDecimal("total");
}
}
}
return BigDecimal.ZERO;
}
}
// Camada de Serviço de Negócio
public static class FuncionarioService {
private final FuncionarioDao dao = new FuncionarioDaoJdbc();
private final String jdbcUrl;
public FuncionarioService(String jdbcUrl) { this.jdbcUrl = jdbcUrl; }
public void contratarFuncionario(Funcionario f) throws SQLException {
if (f.salario().compareTo(new BigDecimal("1500.00")) < 0) {
throw new IllegalArgumentException("Salário contratual abaixo do piso regulatório!");
}
try (Connection conn = DriverManager.getConnection(jdbcUrl, "sa", "")) {
dao.cadastrar(f, conn);
System.out.printf(" ✅ [ADMISSÃO] Colaborador %s registrado no departamento %s!%n", f.nome(), f.dep().sigla());
}
}
public List<Funcionario> consultarQuadroGeral() throws SQLException {
try (Connection conn = DriverManager.getConnection(jdbcUrl, "sa", "")) {
return dao.listarComDepartamento(conn);
}
}
public BigDecimal obterCustoFolha(int depId) throws SQLException {
try (Connection conn = DriverManager.getConnection(jdbcUrl, "sa", "")) {
return dao.calcularFolhaPorDepartamento(depId, conn);
}
}
}
public static void main(String[] args) {
System.out.println("================================================================================");
System.out.println(" PROJETO INTEGRADOR FINAL: ARQUITETURA MULTICAMADAS COMPLETA ");
System.out.println("================================================================================");
String urlBanco = "jdbc:h2:mem:corporativo_integrador;DB_CLOSE_DELAY=-1";
try (Connection conn = DriverManager.getConnection(urlBanco, "sa", "")) {
// DDL de Inicialização
try (Statement st = conn.createStatement()) {
st.execute("""
CREATE TABLE departamentos (
id INT PRIMARY KEY,
sigla VARCHAR(10) NOT NULL,
nome VARCHAR(50) NOT NULL
);
CREATE TABLE funcionarios (
id INT AUTO_INCREMENT PRIMARY KEY,
nome VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL,
salario DECIMAL(15,2) NOT NULL,
data_admissao DATE NOT NULL,
departamento_id INT NOT NULL,
FOREIGN KEY (departamento_id) REFERENCES departamentos(id)
);
INSERT INTO departamentos VALUES (1, 'ENG', 'Engenharia de Software e Cloud');
INSERT INTO departamentos VALUES (2, 'FIN', 'Finanças e Controladoria');
""");
}
FuncionarioService service = new FuncionarioService(urlBanco);
Departamento eng = new Departamento(1, "ENG", "Engenharia de Software e Cloud");
Departamento fin = new Departamento(2, "FIN", "Finanças e Controladoria");
// Cadastro de colaboradores
System.out.println("--- Contratação e Persistência de Dados ---");
service.contratarFuncionario(new Funcionario(0, "Gabriel Toledo", "gabriel@corp.com", new BigDecimal("13500.00"), LocalDate.of(2024, 1, 15), eng));
service.contratarFuncionario(new Funcionario(0, "Larissa Souza", "larissa@corp.com", new BigDecimal("11200.00"), LocalDate.of(2025, 3, 10), eng));
service.contratarFuncionario(new Funcionario(0, "Rodrigo Faro", "rodrigo@corp.com", new BigDecimal("8900.00"), LocalDate.of(2023, 6, 1), fin));
// Relatório Geral via DAO e Projeção
System.out.println("\n--- Relatório Integrado do Quadro Geral ---");
List<Funcionario> lista = service.consultarQuadroGeral();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy");
System.out.printf("%-4s | %-18s | %-18s | %-10s | %-12s%n", "DEP", "COLABORADOR", "EMAIL", "ADMISSÃO", "SALÁRIO");
System.out.println("-----+--------------------+--------------------+------------+-------------");
for (Funcionario f : lista) {
System.out.printf("%-4s | %-18s | %-18s | %s | R$ %9.2f%n",
f.dep().sigla(), f.nome(), f.email(), f.admissao().format(fmt), f.salario());
}
// Agregação de Custos
System.out.println("--------------------------------------------------------------------------");
System.out.printf("Custo Total de Folha (Engenharia) : R$ %,10.2f%n", service.obterCustoFolha(1));
System.out.printf("Custo Total de Folha (Finanças) : R$ %,10.2f%n", service.obterCustoFolha(2));
System.out.println("================================================================================");
} catch (SQLException e) {
System.err.println("Falha no banco de dados integrado: " + e.getMessage());
}
}
}
💻 Exemplo 02 — Carregamento Assíncrono com JavaFX Task
package com.empresa.projeto;
import javafx.concurrent.Task;
import java.util.List;
/**
* Padrão recomendado para carregamento em background prevenindo congelamento de tela.
*/
public class CarregadorAssincronoDados {
public static Task<List<String>> criarTarefaCargaRelatorio() {
return new Task<>() {
@Override
protected List<String> call() throws Exception {
updateMessage("Conectando ao banco de dados relacional...");
Thread.sleep(800); // Simula latência de rede em thread secundária
updateMessage("Executando consulta SQL complexa com agregações...");
updateProgress(50, 100);
Thread.sleep(1000);
updateMessage("Concluído com sucesso!");
updateProgress(100, 100);
return List.of("Registro 01: OK", "Registro 02: OK", "Registro 03: OK");
}
};
}
public static void main(String[] args) {
System.out.println("================================================================================");
System.out.println(" PADRÃO ASSÍNCRONO: JAVAFX TASK COM ATUALIZAÇÃO REATIVA ");
System.out.println("================================================================================");
System.out.println("Tarefa assíncrona instanciada para execução desacoplada da thread visual.");
System.out.println("Ao integrar com JavaFX, use task.setOnSucceeded(...) para atualizar a UI.");
System.out.println("================================================================================");
}
}
📋 Resultado Esperado no Console
================================================================================
PROJETO INTEGRADOR FINAL: ARQUITETURA MULTICAMADAS COMPLETA
================================================================================
--- Contratação e Persistência de Dados ---
✅ [ADMISSÃO] Colaborador Gabriel Toledo registrado no departamento ENG!
✅ [ADMISSÃO] Colaborador Larissa Souza registrado no departamento ENG!
✅ [ADMISSÃO] Colaborador Rodrigo Faro registrado no departamento FIN!
--- Relatório Integrado do Quadro Geral ---
DEP | COLABORADOR | EMAIL | ADMISSÃO | SALÁRIO
-----+--------------------+--------------------+------------+-------------
ENG | Gabriel Toledo | gabriel@corp.com | 15/01/2024 | R$ 13500.00
ENG | Larissa Souza | larissa@corp.com | 10/03/2025 | R$ 11200.00
FIN | Rodrigo Faro | rodrigo@corp.com | 01/06/2023 | R$ 8900.00
--------------------------------------------------------------------------
Custo Total de Folha (Engenharia) : R$ 24.700,00
Custo Total de Folha (Finanças) : R$ 8.900,00
================================================================================