Transactions: COMMIT, ROLLBACK e SAVEPOINT
1. Introdução às Transações no Banco de Dados
Uma transação é uma unidade lógica de trabalho composta por uma ou mais operações SQL que devem ser executadas de forma atômica — ou todas as operações são concluídas com sucesso, ou nenhuma delas surte efeito permanente no banco de dados. Esse conceito é fundamental para garantir a integridade dos dados em sistemas críticos.
As transações obedecem ao princípio ACID:
- Atomicidade: a transação é executada por completo ou é totalmente revertida.
- Consistência: o banco de dados passa de um estado válido para outro estado válido.
- Isolamento: transações concorrentes não interferem entre si.
- Durabilidade: uma vez confirmada, a transação persiste mesmo em caso de falha.
Cenário clássico: transferência bancária entre duas contas. Debitar R$ 500,00 da conta A e creditar R$ 500,00 na conta B são duas operações que devem ocorrer juntas. Se uma falha acontecer após o débito e antes do crédito, o sistema precisaria reverter o débito — para isso servem as transações.
2. Iniciando uma Transação e o Comando COMMIT
Para iniciar uma transação, utiliza-se BEGIN ou START TRANSACTION. Após executar as operações desejadas, o comando COMMIT confirma todas as alterações permanentemente.
BEGIN;
INSERT INTO pedidos (id_cliente, data_pedido, valor_total)
VALUES (101, CURRENT_DATE, 1500.00);
INSERT INTO itens_pedido (id_pedido, id_produto, quantidade, preco_unitario)
VALUES (LASTVAL(), 45, 2, 500.00);
INSERT INTO itens_pedido (id_pedido, id_produto, quantidade, preco_unitario)
VALUES (LASTVAL(), 78, 1, 500.00);
COMMIT;
Neste exemplo, um pedido e seus itens são inseridos em uma única transação. O COMMIT garante que, se todas as inserções forem bem-sucedidas, os dados serão gravados definitivamente. Caso contrário, nenhum registro é persistido.
3. Desfazendo Alterações com ROLLBACK
O comando ROLLBACK reverte todas as alterações feitas desde o início da transação, restaurando o banco ao estado anterior ao BEGIN.
BEGIN;
UPDATE contas SET saldo = saldo - 500 WHERE id_conta = 1;
UPDATE contas SET saldo = saldo + 500 WHERE id_conta = 2;
-- Verificação de consistência: saldo da conta 1 não pode ficar negativo
SELECT saldo FROM contas WHERE id_conta = 1;
-- Suponha que o saldo era 300 e ficou -200 (erro!)
ROLLBACK;
-- As duas operações são desfeitas, o banco volta ao estado original
Se qualquer operação dentro da transação falhar ou violar regras de negócio, o ROLLBACK evita que dados inconsistentes sejam salvos. Isso é essencial em operações financeiras, reservas e qualquer processo que exija integridade referencial.
4. Controle Granular com SAVEPOINT
Nem sempre é necessário reverter toda a transação. Com SAVEPOINT, é possível marcar pontos intermediários e reverter apenas parte do trabalho executado.
BEGIN;
INSERT INTO log_processamento (mensagem) VALUES ('Iniciando lote 1');
SAVEPOINT lote1;
INSERT INTO registros (dado) VALUES ('A');
INSERT INTO registros (dado) VALUES ('B');
-- Erro detectado no lote 1
ROLLBACK TO SAVEPOINT lote1;
-- As inserções de A e B são desfeitas, mas o log permanece
INSERT INTO registros (dado) VALUES ('C');
INSERT INTO registros (dado) VALUES ('D');
SAVEPOINT lote2;
INSERT INTO registros (dado) VALUES ('E');
-- Sucesso no lote 2
RELEASE SAVEPOINT lote2;
-- Libera o ponto de salvamento (opcional, ocorre automaticamente no COMMIT)
COMMIT;
O RELEASE SAVEPOINT remove o ponto de salvamento sem afetar as operações realizadas. É uma boa prática liberar SAVEPOINTs que não serão mais utilizados.
5. Transações Aninhadas e Comportamento do PostgreSQL
Bancos relacionais tratam transações aninhadas de formas diferentes. O PostgreSQL, por exemplo, não suporta transações aninhadas verdadeiras, mas simula esse comportamento com SAVEPOINT.
BEGIN;
INSERT INTO log VALUES ('Transação principal iniciada');
SAVEPOINT ponto_interno;
INSERT INTO dados VALUES (1);
ROLLBACK TO SAVEPOINT ponto_interno;
-- O INSERT acima é desfeito
SAVEPOINT ponto_interno2;
INSERT INTO dados VALUES (2);
-- Mantido
COMMIT;
-- Apenas o valor 2 e o log são persistidos
Diferenças entre bancos:
- PostgreSQL: usa SAVEPOINT para simular aninhamento. ROLLBACK sem especificar TO SAVEPOINT reverte toda a transação.
- MySQL (InnoDB): suporta SAVEPOINT de forma similar, mas ROLLBACK em transações aninhadas pode ter comportamento diferente dependendo da versão.
- Oracle: permite transações aninhadas reais com SAVEPOINT, mas o ROLLBACK sempre reverte até o SAVEPOINT especificado ou até o início da transação.
6. Boas Práticas e Armadilhas Comuns
Evite transações longas: enquanto uma transação está ativa, locks podem ser mantidos em linhas ou tabelas, bloqueando outras operações e reduzindo a concorrência.
-- Ruim: transação longa com pausa manual
BEGIN;
UPDATE contas SET saldo = saldo - 100 WHERE id = 1;
-- Aqui o usuário pensa por 5 minutos...
COMMIT; -- Lock mantido por muito tempo
Cuidado com DDL dentro de transações: comandos como CREATE TABLE, ALTER TABLE e DROP TABLE (DDL) geralmente executam um COMMIT implícito antes e depois de sua execução, encerrando a transação atual.
BEGIN;
INSERT INTO log VALUES ('teste');
CREATE TABLE temp (id INT); -- COMMIT implícito aqui!
INSERT INTO log VALUES ('outro'); -- Nova transação
COMMIT;
-- O primeiro INSERT foi confirmado automaticamente
Tratamento de erros com stored procedures: em funções ou procedimentos armazenados, utilize blocos EXCEPTION para capturar erros e decidir entre ROLLBACK parcial ou total.
Deadlocks: transações que atualizam registros em ordens diferentes podem gerar deadlocks. Sempre acesse tabelas e linhas em uma ordem consistente.
-- Transação 1: atualiza A depois B
BEGIN; UPDATE contas SET saldo = saldo - 10 WHERE id = 1; UPDATE contas SET saldo = saldo + 10 WHERE id = 2; COMMIT;
-- Transação 2: atualiza B depois A (potencial deadlock)
BEGIN; UPDATE contas SET saldo = saldo - 10 WHERE id = 2; UPDATE contas SET saldo = saldo + 10 WHERE id = 1; COMMIT;
7. Exemplo Completo: Sistema de Reservas
O cenário abaixo simula um sistema de reserva de assentos para um evento, onde o usuário pode tentar reservar múltiplos assentos, mas deseja confirmar apenas se todos estiverem disponíveis.
-- Início da transação
BEGIN;
-- Inserir registro da reserva
INSERT INTO reservas (id_usuario, data_reserva, status)
VALUES (42, CURRENT_TIMESTAMP, 'pendente');
-- Obter o ID da reserva recém-criada
-- (em PostgreSQL, usar RETURNING ou CURRVAL)
SAVEPOINT reserva_criada;
-- Tentar reservar assento 1
UPDATE assentos SET id_reserva = LASTVAL(), status = 'ocupado'
WHERE numero = 10 AND id_evento = 5 AND status = 'livre';
IF NOT FOUND THEN
ROLLBACK TO SAVEPOINT reserva_criada;
-- Assento 1 indisponível, reverter tudo
ROLLBACK;
END IF;
-- Tentar reservar assento 2
SAVEPOINT assento2;
UPDATE assentos SET id_reserva = LASTVAL(), status = 'ocupado'
WHERE numero = 11 AND id_evento = 5 AND status = 'livre';
IF NOT FOUND THEN
ROLLBACK TO SAVEPOINT reserva_criada;
-- Assento 2 indisponível, reverter toda a reserva
ROLLBACK;
END IF;
-- Tentar reservar assento 3
SAVEPOINT assento3;
UPDATE assentos SET id_reserva = LASTVAL(), status = 'ocupado'
WHERE numero = 12 AND id_evento = 5 AND status = 'livre';
IF NOT FOUND THEN
ROLLBACK TO SAVEPOINT reserva_criada;
ROLLBACK;
END IF;
-- Todos os assentos reservados com sucesso
UPDATE reservas SET status = 'confirmada' WHERE id = LASTVAL();
RELEASE SAVEPOINT assento3;
RELEASE SAVEPOINT assento2;
RELEASE SAVEPOINT reserva_criada;
COMMIT;
-- Apenas se todos os assentos estiverem disponíveis a reserva é confirmada
Neste exemplo, se qualquer assento estiver ocupado, toda a transação é revertida. O uso de SAVEPOINT permite reverter apenas a tentativa atual e continuar, mas a lógica de negócio optou por reverter tudo para garantir atomicidade.
Referências
- PostgreSQL Documentation: BEGIN, COMMIT, and ROLLBACK — Documentação oficial do PostgreSQL sobre início, confirmação e reversão de transações.
- MySQL 8.0 Reference Manual: COMMIT, ROLLBACK, and SAVEPOINT — Guia completo do MySQL sobre controle de transações e pontos de salvamento.
- Oracle Database SQL Language Reference: SAVEPOINT — Documentação oficial da Oracle sobre o comando SAVEPOINT e comportamento de transações.
- SQL Server Transaction Statements (Transact-SQL) — Referência da Microsoft sobre transações, incluindo BEGIN, COMMIT, ROLLBACK e SAVEPOINT no SQL Server.
- MongoDB: Transactions (Multi-Document ACID Transactions) — Documentação do MongoDB sobre transações multi-documento, com exemplos de COMMIT e ROLLBACK em bancos NoSQL.
- SQLite Documentation: Transaction — Explicação detalhada do SQLite sobre transações, incluindo comportamento de ROLLBACK e SAVEPOINT em bancos embarcados.
- PostgreSQL Transaction Isolation Levels — Artigo técnico sobre níveis de isolamento de transações no PostgreSQL e como eles interagem com COMMIT e ROLLBACK.