Às vezes, transações parecem aqueles personagens de filme de super-herói. Elas salvam nossos bancos de dados do desastre quando rola algum erro, falha ou bug. Se você tá lidando com uma tarefa que precisa de várias operações que não podem ser separadas, as transações garantem que tudo vai rolar junto. Bora ver como isso funciona com um exemplo de processamento de pagamento.
Processando pagamentos
Imagina aquela situação clássica: você tem duas contas bancárias e quer transferir dinheiro de uma pra outra. Não é só apertar um botão e pronto. Tem que garantir que a grana saiu certinho de uma conta e entrou na outra. Qualquer erro pode dar ruim: ou as duas contas ficam sem alteração, ou o saldo fica zoado (tipo, o dinheiro some ou aparece "do nada").
Cenário: Transferência de dinheiro entre contas
Olha só nosso código. Lê com atenção, tipo mensagem de uma galáxia distante:
-- Começando a transação
BEGIN;
-- Passo 1. Tirando dinheiro da conta do remetente
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 1;
-- Passo 2. Adicionando dinheiro na conta do destinatário
UPDATE accounts
SET balance = balance + 100
WHERE account_id = 2;
-- Deu tudo certo? Então salva as mudanças!
COMMIT;
O que é importante aqui?
- Se no
Passo 1ouPasso 2der ruim (tipo erro na query, saldo insuficiente), a transação pode ser desfeita comROLLBACK, e os dados ficam como estavam antes. COMMITgarante que as mudanças só vão ser aplicadas se TODOS os passos derem certo.
Adicionando verificação de saldo
E se o remetente não tiver saldo suficiente pra transferência? Bora colocar uma checagem de saldo pra não deixar ele "no vermelho" sem querer.
-- Começando a transação
BEGIN;
-- Pegando o saldo atual do remetente
DO $$
DECLARE
current_balance NUMERIC;
BEGIN
SELECT balance INTO current_balance FROM accounts WHERE account_id = 1;
-- Checando se tem grana suficiente
IF current_balance >= 100 THEN
-- Se tiver, faz a transferência
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 1;
UPDATE accounts
SET balance = balance + 100
WHERE account_id = 2;
-- Salva as mudanças
COMMIT;
ELSE
-- Se não tiver, desfaz tudo
ROLLBACK;
RAISE NOTICE 'Saldo insuficiente para transferência!';
END IF;
END $$;
O que ficou mais interessante aqui?
- A gente usa um bloco PL/pgSQL com checagem usando
IF. Se o saldo for menor que o necessário, a transação é recusada e nada muda. ROLLBACKdesfaz as mudanças se já tiver começado alguma (nesse caso, nem começou, mas é uma boa prática).
Essa aula é sobre cenários reais de uso de transações, então resolvi trazer um exemplo da vida real. Ele tem uma stored procedure e tá escrito em PL-SQL. Acho que você já tem experiência suficiente pra sacar como tudo funciona aqui. No futuro, a gente volta pra PL-SQL e encara exemplos ainda mais cabulosos.
Atualização em massa de dados numa transação
Criar uma transação não serve só pra transferir dinheiro. Imagina que você tem o banco de dados de uma loja online, onde todo dia vários pedidos mudam de status, tipo de "em entrega" pra "finalizado". Como atualizar vários registros de uma vez só e ainda garantir que, se der ruim, dá pra desfazer tudo? Claro, usando transação.
Bora ver outro cenário: atualização de status dos pedidos.
Olha o exemplo:
-- Começando a transação
BEGIN;
-- Passo 1. Atualizando pedidos com data de entrega já passada
UPDATE orders
SET status = 'completed'
WHERE delivery_date < CURRENT_DATE;
-- Passo 2. Avisando que deu tudo certo
RAISE NOTICE 'Todos os status dos pedidos foram atualizados com sucesso.';
-- Salva as mudanças
COMMIT;
E se der ruim?
Sempre pode rolar um erro. Tipo, você esqueceu de colocar o WHERE e agora todos os pedidos mudaram pra completed. Pra evitar isso, é importante finalizar a transação ou desfazer ela na hora.
Olha o cenário com rollback:
-- Começando a transação
BEGIN;
-- Passo 1. Tentando atualizar pedidos sem condição (ih, erro!)
UPDATE orders
SET status = 'completed';
-- Desfazendo a transação por causa do erro
ROLLBACK;
-- Agora os pedidos ficaram como estavam
Deixando mais "flexível" com SAVEPOINT
Nem sempre você quer desfazer a transação toda. Se seu cenário tem vários passos, talvez você queira desfazer só um deles. É aí que entra o SAVEPOINT.
Agora o cenário é: processar vários passos com a opção de desfazer só um deles.
Imagina que você tá processando um pedido com vários passos: tirando produto do estoque, atualizando status do pedido, mandando notificação pro cliente. Se a notificação não for enviada, você quer desfazer só esse passo, mas manter o resto.
-- Começar a transação
BEGIN;
-- Passo 1. Tirando produto do estoque
UPDATE products
SET stock = stock - 1
WHERE product_id = 101;
-- Salvando ponto de rollback
SAVEPOINT step1;
-- Passo 2. Atualizando status do pedido
UPDATE orders
SET status = 'shipped'
WHERE order_id = 202;
-- Tentando mandar notificação pro cliente
SAVEPOINT step2;
-- Ih, erro na notificação!
ROLLBACK TO SAVEPOINT step2;
-- Decide que é seguro finalizar a transação
COMMIT;
Conclusão
Transações não são só uma ferramenta técnica, são a garantia da integridade dos seus dados. Elas protegem contra o "efeito dominó", quando um erro pode derrubar o sistema todo. Sempre que você fizer várias operações conectadas, se pergunte: "E se uma delas falhar?" Se a resposta for "catástrofe", é hora de usar transação. Lembra: melhor gastar uns minutos escrevendo uma transação do que horas recuperando dados depois de um bug. Seus usuários (e seus nervos) vão agradecer!
GO TO FULL VERSION