Especialização em Sistemas com Java 21 LTS • Módulo de Banco de Dados e Projetos Integradores


🗺️ Mapa Conceitual do Tópico

flowchart TD
    subgraph APRESENTACAO["Camada de Apresentação (UI / JavaFX)"]
        A["Interface Visual FXML + Scene Builder"]
        B["Controller MVC (@FXML Injeção e Eventos)"]
        C["javafx.concurrent.Task (Execução Assíncrona em Background)"]
        A <--> B
        B --> C
    end

    subgraph NEGOCIO["Camada de Serviços & Domínio"]
        D["Entidades de Domínio / Records: Vendedor, Departamento"]
        E["VendedorService (Validações, Regras de Negócio, SLAs)"]
        B --> E
        E --> D
    end

    subgraph PERSISTENCIA["Camada de Persistência & Dados"]
        F["VendedorDao (Interface Contrato)"]
        G["VendedorDaoJdbc (PreparedStatement, SQL, Mapeamento)"]
        H["Conexão JDBC / Connection Pool HikariCP"]
        I["Banco de Dados Relacional (PostgreSQL / MySQL / H2)"]
        E --> F
        G -.->|implementa| F
        G --> H --> I
    end

    C -.->|Platform.runLater| B

    style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
    style B fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
    style E fill:#fff3e0,stroke:#ff9800,stroke-width:2px
    style G fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
    style I fill:#ffebee,stroke:#f44336,stroke-width:2px

🏛️ 1. Arquitetura Multicamadas Corporativa Desktop

O desenvolvimento de sistemas de alta escala em Java exige a separação rigorosa de responsabilidades em camadas concêntricas e desacopladas, impedindo o surgimento de classes gigantes monolíticas (God Objects):

As Seis Camadas Canônicas da Aplicação:

  1. Camada de Visão (View): Arquivos .fxml e folhas de estilo .css. Declaram a geometria, disposição e estética dos componentes visuais.
  2. Camada de Controle (Controller): Classes anotadas com @FXML que capturam cliques de botões, populam tabelas e coordenam a navegação de janelas. Regra de Ouro: Não deve conter nenhuma linha de SQL ou regra pesada de negócio.
  3. Camada de Serviço (Service): Orquestra o fluxo de dados, executa validações de regras corporativas e gerencia a demarcação transacional (commit/rollback).
  4. Camada de Acesso a Dados (DAO): Interfaces e implementações JDBC responsáveis pela montagem de comandos PreparedStatement e conversão de ResultSet em objetos de domínio.
  5. Camada de Modelo (Model / Domain): Entidades ricas e Records imutáveis que representam as tabelas e relacionamentos do negócio.
  6. Camada de Infraestrutura (DB / Connection Factory): Gerenciador de ciclo de vida de conexões físicas com o banco de dados.

⚡ 2. Operações Assíncronas e Prevenção de Travamento da UI (UI Freezing)

Se uma consulta SQL de relatório demorar 5 segundos e for executada diretamente na JavaFX Application Thread, a interface gráfica congelará completamente, o sistema operacional reportará “O programa não está respondendo” e animações serão interrompidas.

A Solução: javafx.concurrent.Task<T>

JavaFX fornece abstrações elegantes para delegar tarefas lentas para threads secundárias (ou Virtual Threads) mantendo a interface reativa:

Task<List<Vendedor>> tarefaCarga = new Task<>() {
    @Override
    protected List<Vendedor> call() throws Exception {
        return vendedorService.listarTodos(); // Executa em background!
    }
};

tarefaCarga.setOnSucceeded(e -> {
    // Retorna automaticamente para a JavaFX Application Thread com segurança
    tabelaVendedores.setItems(FXCollections.observableArrayList(tarefaCarga.getValue()));
    barraProgresso.setVisible(false);
});

tarefaCarga.setOnFailed(e -> {
    AlertaUtil.exibirErro("Falha de Comunicação", tarefaCarga.getException().getMessage());
});

Thread.ofVirtual().start(tarefaCarga); // Executa em Virtual Thread!

🛡️ 3. Transações e Integridade Referencial no CRUD

No cadastro de entidades dependentes (ex: Vendedor associado obrigatoriamente a um Departamento):

  • Integridade Referencial: O DAO deve validar se o departamento_id existe antes de persistir o registro, tratando violações de chave estrangeira (FK constraint violation).
  • Operações de Deleção com Confirmação: Deleções devem apresentar caixas modais de diálogo (Alert(AlertType.CONFIRMATION)) antes de enviar comandos DELETE ao banco.

📊 4. Matriz Comparativa: Padrões de Projeto Desktop

Padrão Separação da Lógica Visual Acoplamento View-Model Complexidade Arquitetural Adoção no JavaFX
MVC Clássico Média (Controller manipula View diretamente) Baixo Baixa Padrão Nativo FXML
MVP (Model-View-Presenter) Alta (View expõe interface ao Presenter) Quase nulo Média Utilizado em aplicações Swing/JavaFX
MVVM (Model-View-ViewModel) Máxima (Data Binding bidirecional) Nulo (ViewModel desconhece a View) Alta Altamente ergonômico com Properties API

💻 5. Código de Demonstração Corporativo (Java 21 LTS)

package com.empresa.projeto;

import java.math.BigDecimal;
import java.sql.*;
import java.time.LocalDate;
import java.util.*;

/**
 * Projeto Integrador Corporativo: Arquitetura completa MVC + DAO + JDBC.
 */
public class SistemaGestaoCorporativaApp {

    // 1. Entidades de Domínio
    public record Departamento(Integer id, String nome) {}
    public record Colaborador(Integer id, String nome, String email, LocalDate dataAdmissao, BigDecimal salario, Departamento departamento) {}

    // 2. Camada DAO (Contrato e Implementação JDBC)
    public interface ColaboradorDao {
        void inserir(Colaborador c, Connection conn) throws SQLException;
        List<Colaborador> listarTodos(Connection conn) throws SQLException;
    }

    public static class ColaboradorDaoJdbc implements ColaboradorDao {
        @Override
        public void inserir(Colaborador c, Connection conn) throws SQLException {
            String sql = "INSERT INTO colaboradores (nome, email, data_admissao, salario, departamento_id) VALUES (?, ?, ?, ?, ?)";
            try (PreparedStatement ps = conn.prepareStatement(sql)) {
                ps.setString(1, c.nome());
                ps.setString(2, c.email());
                ps.setDate(3, java.sql.Date.valueOf(c.dataAdmissao()));
                ps.setBigDecimal(4, c.salario());
                ps.setInt(5, c.departamento().id());
                ps.executeUpdate();
            }
        }

        @Override
        public List<Colaborador> listarTodos(Connection conn) throws SQLException {
            String sql = """
                SELECT c.id, c.nome, c.email, c.data_admissao, c.salario, d.id as dep_id, d.nome as dep_nome
                FROM colaboradores c
                INNER JOIN departamentos d ON c.departamento_id = d.id
                ORDER BY c.nome
            """;
            List<Colaborador> lista = 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_nome"));
                    lista.add(new Colaborador(
                            rs.getInt("id"),
                            rs.getString("nome"),
                            rs.getString("email"),
                            rs.getDate("data_admissao").toLocalDate(),
                            rs.getBigDecimal("salario"),
                            dep
                    ));
                }
            }
            return lista;
        }
    }

    // 3. Camada de Serviço (Regras de Negócio)
    public static class ColaboradorService {
        private final ColaboradorDao dao = new ColaboradorDaoJdbc();
        private final String dbUrl;

        public ColaboradorService(String dbUrl) { this.dbUrl = dbUrl; }

        public void cadastrar(Colaborador c) throws SQLException {
            if (c.salario() == null || c.salario().compareTo(new BigDecimal("1412.00")) < 0) {
                throw new IllegalArgumentException("Salário não pode ser inferior ao piso nacional!");
            }
            try (Connection conn = DriverManager.getConnection(dbUrl, "sa", "")) {
                dao.inserir(c, conn);
            }
        }

        public List<Colaborador> recuperarQuadroGeral() throws SQLException {
            try (Connection conn = DriverManager.getConnection(dbUrl, "sa", "")) {
                return dao.listarTodos(conn);
            }
        }
    }

    // 4. Execução Integrada
    public static void main(String[] args) {
        System.out.println("================================================================================");
        System.out.println("         PROJETO INTEGRADOR: ARQUITETURA MULTICAMADAS JAVA 21                   ");
        System.out.println("================================================================================");

        String urlMemoria = "jdbc:h2:mem:empresadb;DB_CLOSE_DELAY=-1";

        try (Connection conn = DriverManager.getConnection(urlMemoria, "sa", "")) {
            // Provisionamento DDL
            try (Statement st = conn.createStatement()) {
                st.execute("""
                    CREATE TABLE departamentos (id INT PRIMARY KEY, nome VARCHAR(50) NOT NULL);
                    CREATE TABLE colaboradores (
                        id INT AUTO_INCREMENT PRIMARY KEY,
                        nome VARCHAR(100) NOT NULL,
                        email VARCHAR(100) NOT NULL,
                        data_admissao DATE NOT NULL,
                        salario DECIMAL(15,2) NOT NULL,
                        departamento_id INT NOT NULL,
                        FOREIGN KEY (departamento_id) REFERENCES departamentos(id)
                    );
                    INSERT INTO departamentos VALUES (1, 'Engenharia de Software'), (2, 'Operações Financeiras');
                """);
            }
            System.out.println("✅ Banco de dados corporativo estruturado com chaves estrangeiras.");

            ColaboradorService service = new ColaboradorService(urlMemoria);

            // Cadastro de Colaboradores via Camada de Serviço
            Departamento depTI = new Departamento(1, "Engenharia de Software");
            service.cadastrar(new Colaborador(null, "Mariana Ramos", "mariana@empresa.com", LocalDate.now(), new BigDecimal("9800.00"), depTI));
            service.cadastrar(new Colaborador(null, "Lucas Albuquerque", "lucas@empresa.com", LocalDate.now(), new BigDecimal("12500.00"), depTI));

            // Listagem de Produção
            List<Colaborador> equipe = service.recuperarQuadroGeral();
            System.out.printf("%nQuadro de Colaboradores Atualizado (%d registros):%n", equipe.size());
            System.out.printf("%-20s | %-25s | %-22s | %-12s%n", "NOME", "EMAIL", "DEPARTAMENTO", "SALÁRIO");
            System.out.println("---------------------+---------------------------+------------------------+-------------");

            for (Colaborador colab : equipe) {
                System.out.printf("%-20s | %-25s | %-22s | R$ %9.2f%n",
                        colab.nome(), colab.email(), colab.departamento().nome(), colab.salario());
            }

        } catch (Exception e) {
            System.err.println("Erro na execução corporativa: " + e.getMessage());
        }

        System.out.println("================================================================================");
    }
}