W3docs

Transações JDBC em Java

Controle transações de banco de dados em Java JDBC com setAutoCommit, commit, rollback e savepoints.

Uma transação agrupa vários comandos SQL em uma unidade tudo-ou-nada: ou todas as alterações são confirmadas, ou nenhuma é. O exemplo clássico é uma transferência bancária — debitar uma conta, creditar outra — onde aplicar uma sem a outra criaria ou destruiria dinheiro. O JDBC controla transações por meio do objeto Connection.

Este capítulo aborda como abrir uma transação desativando o auto-commit, como commit() e rollback() funcionam, como savepoints permitem desfazer parte de uma transação e como os níveis de isolamento equilibram consistência e concorrência.

Auto-commit está ativado por padrão

Uma conexão recém-criada roda em modo auto-commit: cada comando é confirmado no instante em que termina. Isso é adequado para comandos isolados, mas incorreto para unidades com vários comandos. Para abrir uma transação, desative o auto-commit:

conn.setAutoCommit(false);   // begin a transaction
try {
  // ... several statements ...
  conn.commit();             // make all changes permanent
} catch (SQLException e) {
  conn.rollback();           // undo everything since the last commit
  throw e;
} finally {
  conn.setAutoCommit(true);  // restore default for the pool
}

commit e rollback

commit() torna permanentes e visíveis para outros todas as alterações desde o início da transação. rollback() as descarta todas. A regra de ouro: confirme em caso de sucesso, reverta em qualquer exceção. Esquecer o rollback deixa a conexão segurando bloqueios e uma transação incompleta.

Um exemplo de transferência

A transferência usa dois objetos PreparedStatement, executa ambos os UPDATEs e só confirma depois que os dois foram executados. Se qualquer um lançar uma exceção, o catch reverte tudo para que nenhum dinheiro seja criado ou perdido:

conn.setAutoCommit(false);
try (PreparedStatement debit = conn.prepareStatement(
        "UPDATE acct SET bal = bal - ? WHERE id = ?");
     PreparedStatement credit = conn.prepareStatement(
        "UPDATE acct SET bal = bal + ? WHERE id = ?")) {
  debit.setBigDecimal(1, amount); debit.setInt(2, fromId); debit.executeUpdate();
  credit.setBigDecimal(1, amount); credit.setInt(2, toId); credit.executeUpdate();
  conn.commit();
} catch (SQLException e) {
  conn.rollback();
  throw e;
}

Savepoints: rollback parcial

Um Savepoint é um marcador dentro de uma transação para o qual você pode reverter, desfazendo trabalho posterior enquanto mantém o trabalho anterior. Isso é útil quando uma etapa opcional dentro de uma unidade maior pode falhar, mas você não quer abandonar a transação inteira:

Savepoint sp = conn.setSavepoint("afterDebit");
// ... risky step ...
conn.rollback(sp);   // undo back to the savepoint, not the whole transaction

Duas regras para lembrar: reverter para um savepoint não encerra a transação — você ainda precisa de commit() ou de um rollback() completo depois — e você deve liberar savepoints desnecessários com conn.releaseSavepoint(sp) para liberar recursos do banco de dados. Savepoints só existem enquanto o auto-commit está desativado.

Níveis de isolamento

setTransactionIsolation(...) equilibra consistência e concorrência. Do mais fraco ao mais forte: READ_UNCOMMITTED (vê linhas "sujas" não confirmadas de outras transações), READ_COMMITTED (o padrão comum), REPEATABLE_READ (releituras veem as mesmas linhas) e SERIALIZABLE (as transações se comportam como se fossem executadas uma de cada vez). Níveis mais fortes evitam mais anomalias, mas permitem menos paralelismo.

As anomalias que cada nível protege, em ordem:

  • Dirty read — ler uma linha que outra transação alterou mas não confirmou. Prevenido a partir de READ_COMMITTED.
  • Non-repeatable read — reler a mesma linha e obter um valor diferente porque outra transação confirmou uma alteração nesse intervalo. Prevenido a partir de REPEATABLE_READ.
  • Phantom read — executar novamente a mesma consulta e obter linhas extras porque outra transação inseriu linhas correspondentes. Prevenido apenas por SERIALIZABLE.

Defina o nível antes do início do trabalho e verifique o que seu banco de dados realmente suporta com DatabaseMetaData.supportsTransactionIsolationLevel(...) — nem todo driver respeita todos os níveis.

Um exemplo completo: níveis de isolamento e a unidade de trabalho

Este programa imprime as quatro constantes de nível de isolamento em ordem crescente de força, depois modela uma transferência como uma unidade atômica — preparando duas atualizações e confirmando ambas ou revertendo ambas — espelhando o ciclo setAutoCommit/commit/rollback sem um banco de dados real.

java— editable, runs on the server

O que extrair da execução:

  • As constantes de isolamento crescem em força: READ_UNCOMMITTED (1), READ_COMMITTED (2), REPEATABLE_READ (4), SERIALIZABLE (8). Você passa uma delas a setTransactionIsolation para escolher quanta anomalia de concorrência tolerará.
  • A transferência prepara ambas as atualizações antes de confirmar. Essa é a essência de uma transação: os dois UPDATEs formam uma unidade, e o commit() só ocorre quando ambos estão prontos.
  • O passo commit() aqui move ambos os comandos de pending para committed juntos — modelando como o banco de dados torna todas as alterações duráveis e visíveis no mesmo instante, nunca pela metade.
  • O catch limpa pending — o substituto do rollback(). A lição é a disciplina: qualquer exceção dentro da transação deve levar a um rollback, ou você deixa o banco de dados em um estado parcialmente aplicado.
  • transfer applied: true reflete uma confirmação bem-sucedida. Inverta a lógica para que a guarda falhe e você verá um rollback sem nada aplicado — exatamente a garantia tudo-ou-nada que uma transação existe para oferecer.

Prática

Prática
Você define conn.setAutoCommit(false), executa dois comandos UPDATE e o segundo lança uma SQLException. O que seu bloco catch deve fazer para preservar a integridade dos dados?
Você define conn.setAutoCommit(false), executa dois comandos UPDATE e o segundo lança uma SQLException. O que seu bloco catch deve fazer para preservar a integridade dos dados?

Tópicos relacionados

  • Conexão JDBC — onde vivem auto-commit, commit e rollback.
  • PreparedStatement — a forma segura de executar atualizações parametrizadas dentro de uma transação.
  • Processamento em lote — agrupe muitos comandos; lotes e transações frequentemente trabalham juntos.
  • DatabaseMetaData — consulte quais níveis de isolamento seu driver suporta.
Was this page helpful?