Dominando Transações em PDO: Garantindo Integridade dos Dados em Projetos Reais Já leu

O que são Transações em PDO? Uma transação é um conjunto de operações no banco de dados que deve ser executado completamente ou não ser executado. O PDO (PHP Data Objects) fornece mecanismos simples para gerenciar transações, garantindo que dados inconsistentes nunca sejam salvos. Imagine uma transferência bancária: você precisa debitar de uma conta e creditar em outra. Se apenas uma operação for concluída, os dados ficarão corrompidos. Transações resolvem exatamente isso, funcionando sob o princípio ACID (Atomicidade, Consistência, Isolamento e Durabilidade). No PDO, você controla transações manualmente usando , e . Se algo der errado durante a execução, pode desfazer todas as alterações com um único comando. Isso é essencial para operações críticas onde a integridade dos dados é não-negociável. Iniciando e Controlando Transações Estrutura Básica O fluxo de uma transação é simples: inicie, execute operações e finalize com confirmação ou desfaça tudo em caso de erro. O PDO oferece três métodos fundamentais: inicia a transação, confirma as alterações,

O que são Transações em PDO?

Uma transação é um conjunto de operações no banco de dados que deve ser executado completamente ou não ser executado. O PDO (PHP Data Objects) fornece mecanismos simples para gerenciar transações, garantindo que dados inconsistentes nunca sejam salvos. Imagine uma transferência bancária: você precisa debitar de uma conta e creditar em outra. Se apenas uma operação for concluída, os dados ficarão corrompidos. Transações resolvem exatamente isso, funcionando sob o princípio ACID (Atomicidade, Consistência, Isolamento e Durabilidade).

No PDO, você controla transações manualmente usando beginTransaction(), commit() e rollback(). Se algo der errado durante a execução, pode desfazer todas as alterações com um único comando. Isso é essencial para operações críticas onde a integridade dos dados é não-negociável.

Iniciando e Controlando Transações

Estrutura Básica

O fluxo de uma transação é simples: inicie, execute operações e finalize com confirmação ou desfaça tudo em caso de erro. O PDO oferece três métodos fundamentais: beginTransaction() inicia a transação, commit() confirma as alterações, e rollback() desfaz tudo.

<?php
try {
    $pdo = new PDO('mysql:host=localhost;dbname=banco', 'usuario', 'senha');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    $pdo->beginTransaction();

    // Operação 1: Debitar da conta A
    $stmt = $pdo->prepare('UPDATE contas SET saldo = saldo - ? WHERE id = ?');
    $stmt->execute([100, 1]);

    // Operação 2: Creditar na conta B
    $stmt = $pdo->prepare('UPDATE contas SET saldo = saldo + ? WHERE id = ?');
    $stmt->execute([100, 2]);

    $pdo->commit();
    echo "Transferência realizada com sucesso!";

} catch (Exception $e) {
    $pdo->rollBack();
    echo "Erro: " . $e->getMessage();
}
?>

Verificando o Estado da Transação

Às vezes você precisa verificar se uma transação está ativa. O método inTransaction() retorna um booleano indicando o status. Isso é útil em operações encadeadas ou quando múltiplas funções manipulam o banco.

<?php
if ($pdo->inTransaction()) {
    echo "Uma transação está em andamento";
    $pdo->rollBack();
} else {
    $pdo->beginTransaction();
    // suas operações aqui
    $pdo->commit();
}
?>

Tratamento de Erros e Exceções

Configurando o Modo de Erro

A forma como o PDO reporta erros afeta diretamente como suas transações funcionam. O modo PDO::ERRMODE_EXCEPTION é recomendado para trabalhar com transações, pois lança exceções que podem ser capturadas e tratadas elegantemente.

<?php
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try {
    $pdo->beginTransaction();

    $stmt = $pdo->prepare('INSERT INTO usuarios (email, nome) VALUES (?, ?)');
    $stmt->execute(['teste@email.com', 'João Silva']);

    // Simulando um erro deliberado
    $stmt = $pdo->prepare('INSERT INTO logs (user_id, acao) VALUES (?, ?)');
    $stmt->execute([99999, 'login']); // user_id inexistente causa erro

    $pdo->commit();

} catch (PDOException $e) {
    $pdo->rollBack();
    error_log("Transação falhou: " . $e->getMessage());
    echo "Operação não pôde ser concluída. Tente novamente.";
}
?>

Tratamento Granular

Em operações complexas, você pode precisar de tratamento mais específico. Use diferentes catch blocks ou verifique códigos de erro SQL antes de decidir se vai fazer rollback.

<?php
try {
    $pdo->beginTransaction();

    $stmt = $pdo->prepare('UPDATE produtos SET estoque = estoque - ? WHERE id = ?');
    $stmt->execute([5, 10]);

    if ($stmt->rowCount() === 0) {
        throw new Exception("Produto não encontrado");
    }

    $pdo->commit();

} catch (PDOException $e) {
    $pdo->rollBack();
    echo "Erro no banco de dados: " . $e->errorInfo[2];
} catch (Exception $e) {
    $pdo->rollBack();
    echo "Validação falhou: " . $e->getMessage();
}
?>

Casos de Uso e Boas Práticas

Quando Usar Transações

Transações são essenciais em operações que envolvem múltiplos passos interdependentes. Operações financeiras, criação de pedidos com itens associados, ou atualizações em cascata são cenários clássicos. Porém, não abuse: transações consomem recursos e podem causar deadlocks se mal implementadas.

<?php
function criarPedido($pdo, $usuarioId, $itens) {
    try {
        $pdo->beginTransaction();

        // Criar pedido
        $stmt = $pdo->prepare('INSERT INTO pedidos (usuario_id, data, total) VALUES (?, NOW(), ?)');
        $total = array_sum(array_column($itens, 'preco'));
        $stmt->execute([$usuarioId, $total]);
        $pedidoId = $pdo->lastInsertId();

        // Inserir itens
        $stmt = $pdo->prepare('INSERT INTO pedido_itens (pedido_id, produto_id, quantidade, preco) VALUES (?, ?, ?, ?)');
        foreach ($itens as $item) {
            $stmt->execute([$pedidoId, $item['produto_id'], $item['quantidade'], $item['preco']]);
        }

        // Atualizar estoque
        $stmt = $pdo->prepare('UPDATE produtos SET estoque = estoque - ? WHERE id = ?');
        foreach ($itens as $item) {
            $stmt->execute([$item['quantidade'], $item['produto_id']]);
        }

        $pdo->commit();
        return ['sucesso' => true, 'pedido_id' => $pedidoId];

    } catch (Exception $e) {
        $pdo->rollBack();
        return ['sucesso' => false, 'erro' => $e->getMessage()];
    }
}
?>

Nível de Isolamento

Diferentes bancos de dados oferecem níveis de isolamento que afetam como transações simultâneas interagem. O padrão geralmente é adequado, mas em casos críticos você pode ajustar:

<?php
$pdo->exec("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
$pdo->beginTransaction();
// suas operações com garantias mais fortes
$pdo->commit();
?>

Conclusão

Transações em PDO são o mecanismo fundamental para garantir integridade de dados em aplicações sérias. O ponto crucial é sempre envolver operações interdependentes em blocos try-catch, iniciando com beginTransaction() e finalizando com commit() ou rollBack(). Segundo, configure seu PDO para modo de exceções (PDO::ERRMODE_EXCEPTION) — isso torna o código mais previsível e fácil de debugar. Terceiro, não use transações indiscriminadamente; reserve-as para operações realmente críticas onde a consistência é não-negociável, evitando gargalos de performance.

Referências


Artigos relacionados