💻 Exemplos: Capítulo 17: Acesso a Dados com JDBC, Transações ACID, PreparedStatement e DAO Pattern
Especialização em Sistemas com Java 21 LTS • Capítulo 17
🎯 Visão Geral dos Exemplos Práticos
Neste laboratório prático, implementaremos exemplos corporativos completos explorando:
- Transações Bancárias ACID com
commiterollback: transferência monetária atômica entre contas com tratamento de inconsistências. - Camada de Acesso a Dados com o Padrão DAO: encapsulamento de comandos SQL com
PreparedStatemente recuperação de chaves auto-incrementais (getGeneratedKeys). - Prevenção Concreta contra SQL Injection: comparação de sanitização via bind de parâmetros posicionais (
?).
💻 Exemplo 01 — Sistema Bancário de Transferências ACID com JDBC
package com.empresa.persistencia;
import java.math.BigDecimal;
import java.sql.*;
/**
* Motor de transferências financeiras com atomicidade garantida e isolamento transacional.
*/
public class TransacaoBancariaAcidJdbc {
private static final String JDBC_URL = "jdbc:h2:mem:bancodb;DB_CLOSE_DELAY=-1";
public static void inicializarBanco(Connection conn) throws SQLException {
try (Statement st = conn.createStatement()) {
st.execute("""
CREATE TABLE contas (
numero_conta VARCHAR(20) PRIMARY KEY,
titular VARCHAR(100) NOT NULL,
saldo DECIMAL(15,2) NOT NULL
);
INSERT INTO contas VALUES ('CC-1001', 'Alice Santos', 5000.00);
INSERT INTO contas VALUES ('CC-1002', 'Bruno Rocha', 1200.00);
""");
}
System.out.println("✅ Tabela 'contas' inicializada com dados de teste.");
}
public static boolean realizarTransferencia(
Connection conn,
String contaOrigem,
String contaDestino,
BigDecimal valor) {
String sqlVerifica = "SELECT saldo FROM contas WHERE numero_conta = ? FOR UPDATE";
String sqlDebito = "UPDATE contas SET saldo = saldo - ? WHERE numero_conta = ?";
String sqlCredito = "UPDATE contas SET saldo = saldo + ? WHERE numero_conta = ?";
try {
// 1. Desativar commit automático para abrir fronteira transacional ACID
conn.setAutoCommit(false);
// 2. Checagem de saldo com lock exclusivo (FOR UPDATE)
try (PreparedStatement psVerifica = conn.prepareStatement(sqlVerifica)) {
psVerifica.setString(1, contaOrigem);
try (ResultSet rs = psVerifica.executeQuery()) {
if (!rs.next()) {
throw new SQLException("Conta de origem não encontrada: " + contaOrigem);
}
BigDecimal saldoAtual = rs.getBigDecimal("saldo");
if (saldoAtual.compareTo(valor) < 0) {
throw new SQLException(String.format("Saldo insuficiente: Saldo R$ %.2f, Saque R$ %.2f", saldoAtual, valor));
}
}
}
// 3. Execução do Débito
try (PreparedStatement psDebito = conn.prepareStatement(sqlDebito)) {
psDebito.setBigDecimal(1, valor);
psDebito.setString(2, contaOrigem);
psDebito.executeUpdate();
}
// Simulação de possível falha ou regra de negócio restritiva
if (valor.compareTo(new BigDecimal("10000.00")) > 0) {
throw new SQLException("Valor excede o limite diário de transferência sem validação prévia!");
}
// 4. Execução do Crédito
try (PreparedStatement psCredito = conn.prepareStatement(sqlCredito)) {
psCredito.setBigDecimal(1, valor);
psCredito.setString(2, contaDestino);
psCredito.executeUpdate();
}
// 5. Confirmação atômica de todas as instruções
conn.commit();
System.out.printf(" ✅ [COMMIT] Transferência de R$ %.2f de %s para %s liquidada com sucesso!%n",
valor, contaOrigem, contaDestino);
return true;
} catch (SQLException e) {
try {
// Em caso de qualquer erro, desfazemos todas as alterações
conn.rollback();
System.err.printf(" 🚨 [ROLLBACK] Transação desfeita com integridade preservada: %s%n", e.getMessage());
} catch (SQLException rollbackEx) {
System.err.println("Falha catastrófica ao tentar rollback: " + rollbackEx.getMessage());
}
return false;
} finally {
try {
conn.setAutoCommit(true); // Restaura o comportamento padrão
} catch (SQLException ignored) {}
}
}
public static void exibirSaldos(Connection conn) throws SQLException {
String sql = "SELECT numero_conta, titular, saldo FROM contas ORDER BY numero_conta";
try (Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql)) {
System.out.println("--------------------------------------------------------------------------------");
while (rs.next()) {
System.out.printf(" Conta: %s | Titular: %-15s | Saldo: R$ %9.2f%n",
rs.getString("numero_conta"), rs.getString("titular"), rs.getBigDecimal("saldo"));
}
System.out.println("--------------------------------------------------------------------------------");
}
}
public static void main(String[] args) {
System.out.println("================================================================================");
System.out.println(" SIMULADOR BANCÁRIO JDBC: TRANSAÇÕES ACID COM ROLLBACK ");
System.out.println("================================================================================");
try (Connection conn = DriverManager.getConnection(JDBC_URL, "sa", "")) {
inicializarBanco(conn);
exibirSaldos(conn);
// Cenário 1: Transferência legítima de R$ 1.500
System.out.println("--- 1. Executando Transferência Válida (R$ 1.500,00) ---");
realizarTransferencia(conn, "CC-1001", "CC-1002", new BigDecimal("1500.00"));
exibirSaldos(conn);
// Cenário 2: Transferência com saldo insuficiente (R$ 8.000)
System.out.println("--- 2. Tentativa com Saldo Insuficiente (R$ 8.000,00) ---");
realizarTransferencia(conn, "CC-1001", "CC-1002", new BigDecimal("8000.00"));
exibirSaldos(conn); // Saldos devem permanecer inalterados!
// Cenário 3: Transferência que excede teto de segurança (R$ 15.000)
System.out.println("--- 3. Tentativa Excedendo Limite Operacional (R$ 15.000,00) ---");
realizarTransferencia(conn, "CC-1001", "CC-1002", new BigDecimal("15000.00"));
exibirSaldos(conn);
} catch (SQLException e) {
System.err.println("Erro geral de conexão: " + e.getMessage());
}
System.out.println("================================================================================");
}
}
📋 Resultado Esperado no Console
================================================================================
SIMULADOR BANCÁRIO JDBC: TRANSAÇÕES ACID COM ROLLBACK
================================================================================
✅ Tabela 'contas' inicializada com dados de teste.
--------------------------------------------------------------------------------
Conta: CC-1001 | Titular: Alice Santos | Saldo: R$ 5000.00
Conta: CC-1002 | Titular: Bruno Rocha | Saldo: R$ 1200.00
--------------------------------------------------------------------------------
--- 1. Executando Transferência Válida (R$ 1.500,00) ---
✅ [COMMIT] Transferência de R$ 1500.00 de CC-1001 para CC-1002 liquidada com sucesso!
--------------------------------------------------------------------------------
Conta: CC-1001 | Titular: Alice Santos | Saldo: R$ 3500.00
Conta: CC-1002 | Titular: Bruno Rocha | Saldo: R$ 2700.00
--------------------------------------------------------------------------------
--- 2. Tentativa com Saldo Insuficiente (R$ 8.000,00) ---
🚨 [ROLLBACK] Transação desfeita com integridade preservada: Saldo insuficiente: Saldo R$ 3500.00, Saque R$ 8000.00
--------------------------------------------------------------------------------
Conta: CC-1001 | Titular: Alice Santos | Saldo: R$ 3500.00
Conta: CC-1002 | Titular: Bruno Rocha | Saldo: R$ 2700.00
--------------------------------------------------------------------------------
--- 3. Tentativa Excedendo Limite Operacional (R$ 15.000,00) ---
🚨 [ROLLBACK] Transação desfeita com integridade preservada: Valor excede o limite diário de transferência sem validação prévia!
--------------------------------------------------------------------------------
Conta: CC-1001 | Titular: Alice Santos | Saldo: R$ 3500.00
Conta: CC-1002 | Titular: Bruno Rocha | Saldo: R$ 2700.00
--------------------------------------------------------------------------------
================================================================================