O que é uma Transação?
Definição
Uma transação é uma unidade lógica de trabalho que consiste em uma ou mais operações SQL que devem ser executadas como um todo indivisível.
A transação garante que todas as operações sejam concluídas com sucesso ou, em caso de falha, todas sejam desfeitas (tudo ou nada).
Propriedades ACID:
- Atomicidade: Tudo é executado ou nada é executado
- Consistência: O banco passa de um estado válido para outro estado válido
- Isolamento: Transações concorrentes não interferem entre si
- Durabilidade: Uma vez confirmada (COMMIT), a transação é permanente
Exemplo Real: Transferência Bancária
Transferir R$ 1.000 da conta A para conta B envolve duas operações:
- Debitar R$ 1.000 da conta A
- Creditar R$ 1.000 na conta B
Se a operação 1 suceder mas a 2 falhar, o dinheiro "desaparece". Uma transação garante que ambas aconteçam ou nenhuma aconteça.
Início e Fim de uma Transação no Oracle
No Oracle, uma transação:
- Inicia automaticamente com a primeira instrução DML (INSERT, UPDATE, DELETE)
- Termina com COMMIT (confirma) ou ROLLBACK (cancela)
- Também termina com comandos DDL (CREATE, ALTER, DROP) - que fazem COMMIT implícito
- Também termina ao desconectar da sessão
COMMIT - Confirmar Alterações
Como funciona o COMMIT
O comando COMMIT torna permanentes todas as alterações
feitas na transação atual. Após o COMMIT:
- As alterações são gravadas definitivamente no banco de dados
- Outros usuários podem ver as alterações
- Os locks (bloqueios) são liberados
- Uma nova transação começa automaticamente
-- Exemplo básico de COMMIT
UPDATE funcionarios
SET salario = salario * 1.10
WHERE departamento_id = 10;
-- Verificar quantas linhas foram afetadas
-- (5 linhas atualizadas)
COMMIT; -- Confirma as alterações
-- Agora as alterações são permanentes e visíveis para todos
COMMIT Implícito (Autocommit)
Alguns comandos fazem COMMIT automático no Oracle:
Comandos que causam COMMIT implícito:
- DDL: CREATE, ALTER, DROP, TRUNCATE, RENAME
- DCL: GRANT, REVOKE
- Saída normal: EXIT ou desconexão normal
-- Exemplo de COMMIT implícito
UPDATE funcionarios SET salario = 5000 WHERE id = 10; -- Transação iniciada
CREATE TABLE teste (id NUMBER); -- DDL faz COMMIT automático!
-- A atualização acima foi confirmada automaticamente
ROLLBACK; -- Não vai desfazer o UPDATE, pois já foi commitado!
ROLLBACK - Desfazer Alterações
Como funciona o ROLLBACK
O comando ROLLBACK desfaz todas as alterações
feitas na transação atual desde o último COMMIT. Após o ROLLBACK:
- Todas as alterações são descartadas
- O banco retorna ao estado anterior ao início da transação
- Os locks são liberados
- Uma nova transação começa automaticamente
-- Exemplo básico de ROLLBACK
UPDATE funcionarios
SET salario = salario * 2
WHERE departamento_id = 10;
SELECT salario FROM funcionarios WHERE departamento_id = 10;
-- Salários dobrados (ainda não confirmado)
ROLLBACK; -- Desfaz as alterações
SELECT salario FROM funcionarios WHERE departamento_id = 10;
-- Salários voltaram aos valores originais
Quando usar ROLLBACK
1. Erro detectado manualmente
DELETE FROM funcionarios WHERE departamento_id = 10;
-- Ops! Queria excluir apenas um funcionário!
ROLLBACK; -- Desfaz a exclusão
2. Erro de constraint
INSERT INTO funcionarios VALUES (1, 'João', 'joao@email.com', SYSDATE, 5000, 99);
-- ORA-02291: parent key not found (departamento 99 não existe)
ROLLBACK; -- Descarta a tentativa de inserção
3. ROLLBACK automático
O Oracle faz ROLLBACK automático se:
- A sessão é encerrada anormalmente (crash, perda de conexão)
- Ocorre um erro grave no sistema
SAVEPOINT - Pontos de Salvamento
O que é um SAVEPOINT?
Um SAVEPOINT é um marcador dentro de uma transação
que permite fazer ROLLBACK parcial, sem desfazer toda a transação.
-- Criando savepoints
INSERT INTO departamentos VALUES (40, 'Marketing', 'Rio de Janeiro', 150000);
SAVEPOINT sp1; -- Primeiro ponto de salvamento
UPDATE funcionarios SET salario = salario * 1.05 WHERE departamento_id = 10;
SAVEPOINT sp2; -- Segundo ponto de salvamento
DELETE FROM funcionarios WHERE id_funcionario = 999;
SAVEPOINT sp3; -- Terceiro ponto de salvamento
-- Desfazer apenas até sp2 (mantém INSERT e UPDATE, desfaz DELETE)
ROLLBACK TO SAVEPOINT sp2;
-- Ou desfazer até sp1 (mantém apenas INSERT)
ROLLBACK TO SAVEPOINT sp1;
-- Ou desfazer tudo
ROLLBACK;
Exemplo Prático: Processo de Migração de Dados
-- Processo complexo com múltiplos passos
BEGIN
-- Passo 1: Criar novos registros
INSERT INTO departamentos_novo
SELECT * FROM departamentos_antigo;
SAVEPOINT passo1_completo;
-- Passo 2: Atualizar referências
UPDATE funcionarios
SET departamento_id = departamento_id + 1000;
SAVEPOINT passo2_completo;
-- Passo 3: Validar dados
UPDATE funcionarios
SET status = 'MIGRADO';
-- Se algo deu errado no passo 3, voltar para passo2
-- ROLLBACK TO SAVEPOINT passo2_completo;
-- Se tudo OK, confirmar tudo
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK; -- Desfaz tudo em caso de erro
RAISE;
END;
/
Dica
Savepoints são muito úteis em procedures e processos longos onde você quer manter controle granular sobre o que foi feito, sem perder todo o trabalho em caso de erro.
Isolamento de Transações
Conceito de Isolamento
O isolamento garante que transações simultâneas não interfiram entre si. O Oracle usa locks (bloqueios) e controle de concorrência multiversão (MVCC).
Como o Oracle gerencia concorrência:
- Leitores não bloqueiam escritores (queries não travam updates)
- Escritores não bloqueiam leitores (updates não travam queries)
- Escritores bloqueiam escritores (updates concorrentes na mesma linha esperam)
- Consistência de leitura: queries veem snapshot consistente do banco
Exemplo de Concorrência
Sessão 1
-- T1: Iniciar transação
UPDATE funcionarios
SET salario = 6000
WHERE id = 10;
-- Aguardando...
-- (não fez COMMIT ainda)
-- T3: Confirmar
COMMIT;
Sessão 2
-- T2: Tentar atualizar
-- mesma linha
UPDATE funcionarios
SET salario = 7000
WHERE id = 10;
-- ESPERA! Sessão 1 tem lock
-- T4: Após COMMIT da Sessão 1
-- Agora executa e pega lock
Observação Importante
Enquanto isso, outras sessões fazendo SELECT na linha veem o valor ANTIGO (antes do UPDATE), garantindo consistência de leitura. Após o COMMIT, elas veem o novo valor.
Exemplos Práticos Completos
Exemplo 1: Transferência entre Contas
-- Transferir R$ 1000 da conta 100 para conta 200
SAVEPOINT inicio_transferencia;
-- Debitar conta origem
UPDATE contas
SET saldo = saldo - 1000
WHERE numero_conta = 100;
-- Verificar se ficou negativo
SELECT saldo INTO v_saldo
FROM contas
WHERE numero_conta = 100;
IF v_saldo < 0 THEN
ROLLBACK TO SAVEPOINT inicio_transferencia;
RAISE_APPLICATION_ERROR(-20001, 'Saldo insuficiente');
END IF;
-- Creditar conta destino
UPDATE contas
SET saldo = saldo + 1000
WHERE numero_conta = 200;
-- Registrar histórico
INSERT INTO historico_transferencias VALUES
(seq_transf.NEXTVAL, 100, 200, 1000, SYSDATE);
-- Confirmar toda a transação
COMMIT;
Exemplo 2: Importação de Dados com Validação
-- Importar dados em lote com validação
DECLARE
v_erros NUMBER := 0;
BEGIN
SAVEPOINT inicio_importacao;
-- Inserir dados temporários
INSERT INTO funcionarios_temp
SELECT * FROM arquivo_externo;
-- Validar dados
SELECT COUNT(*) INTO v_erros
FROM funcionarios_temp
WHERE email NOT LIKE '%@%'
OR salario <= 0
OR departamento_id NOT IN (SELECT id_departamento FROM departamentos);
IF v_erros > 0 THEN
DBMS_OUTPUT.PUT_LINE('Encontrados ' || v_erros || ' registros inválidos');
ROLLBACK TO SAVEPOINT inicio_importacao;
ELSE
-- Mover dados validados para tabela final
INSERT INTO funcionarios
SELECT * FROM funcionarios_temp;
COMMIT;
DBMS_OUTPUT.PUT_LINE('Importação concluída com sucesso');
END IF;
END;
/
Exemplo 3: Atualização com Auditoria
-- Atualizar salários com registro de auditoria
BEGIN
-- Registrar estado anterior
INSERT INTO auditoria_salarios
SELECT id_funcionario, salario, USER, SYSDATE, 'ANTES'
FROM funcionarios
WHERE departamento_id = 10;
SAVEPOINT antes_atualizacao;
-- Aplicar aumento
UPDATE funcionarios
SET salario = salario * 1.10
WHERE departamento_id = 10;
-- Registrar estado posterior
INSERT INTO auditoria_salarios
SELECT id_funcionario, salario, USER, SYSDATE, 'DEPOIS'
FROM funcionarios
WHERE departamento_id = 10;
-- Verificar se ultrapassou orçamento
SELECT SUM(salario) INTO v_total
FROM funcionarios
WHERE departamento_id = 10;
IF v_total > v_orcamento_depto THEN
ROLLBACK TO SAVEPOINT antes_atualizacao;
RAISE_APPLICATION_ERROR(-20002, 'Orçamento ultrapassado');
ELSE
COMMIT;
END IF;
END;
/
Resultado Esperado
O que você deve dominar após este tópico:
Entender transações
Conceito de transação e propriedades ACID
Usar COMMIT corretamente
Saber quando confirmar alterações
Usar ROLLBACK efetivamente
Desfazer alterações quando necessário
Criar SAVEPOINTs
Controlar transações complexas com pontos intermediários
Entender isolamento
Conceitos de locks e concorrência
Evitar commits implícitos
Conhecer comandos que fazem autocommit
Exercícios Práticos
Exercício 1: Simular Erro e Rollback
Execute uma série de INSERTs em uma tabela, depois faça ROLLBACK e verifique que os dados não foram salvos.
Ver Solução
-- Inserir dados de teste
INSERT INTO funcionarios VALUES (1000, 'Teste 1', 'teste1@email.com', SYSDATE, 3000, 10);
INSERT INTO funcionarios VALUES (1001, 'Teste 2', 'teste2@email.com', SYSDATE, 3500, 10);
-- Verificar inserção
SELECT * FROM funcionarios WHERE id_funcionario >= 1000;
-- (2 linhas retornadas)
-- Desfazer
ROLLBACK;
-- Verificar que não foram salvos
SELECT * FROM funcionarios WHERE id_funcionario >= 1000;
-- (0 linhas - dados desapareceram)
Exercício 2: Usar SAVEPOINTs
Crie 3 savepoints em uma transação e faça rollback parcial para diferentes pontos.
Ver Solução
-- Passo 1
INSERT INTO departamentos VALUES (50, 'Vendas', 'Curitiba', 180000);
SAVEPOINT passo1;
-- Passo 2
INSERT INTO departamentos VALUES (60, 'Suporte', 'Brasília', 120000);
SAVEPOINT passo2;
-- Passo 3
INSERT INTO departamentos VALUES (70, 'Logística', 'Manaus', 100000);
SAVEPOINT passo3;
-- Desfazer apenas passo 3
ROLLBACK TO SAVEPOINT passo2;
SELECT * FROM departamentos WHERE id_departamento >= 50;
-- (Apenas 50 e 60 existem)
-- Confirmar
COMMIT;
Exercício 3: Transação Bancária
Implemente uma transferência entre duas contas com validação de saldo e rollback em caso de erro.
Ver Solução
DECLARE
v_saldo_origem NUMBER;
v_valor_transf NUMBER := 500;
BEGIN
SAVEPOINT inicio_transf;
-- Verificar saldo
SELECT saldo INTO v_saldo_origem
FROM contas
WHERE numero_conta = 1001;
IF v_saldo_origem < v_valor_transf THEN
RAISE_APPLICATION_ERROR(-20001, 'Saldo insuficiente');
END IF;
-- Débito
UPDATE contas
SET saldo = saldo - v_valor_transf
WHERE numero_conta = 1001;
-- Crédito
UPDATE contas
SET saldo = saldo + v_valor_transf
WHERE numero_conta = 1002;
COMMIT;
DBMS_OUTPUT.PUT_LINE('Transferência realizada com sucesso');
EXCEPTION
WHEN OTHERS THEN
ROLLBACK TO SAVEPOINT inicio_transf;
DBMS_OUTPUT.PUT_LINE('Erro: ' || SQLERRM);
END;
/