W3docs

Transacciones JDBC en Java

Controla transacciones de base de datos en Java JDBC con setAutoCommit, commit, rollback y savepoints.

Una transacción agrupa varias sentencias SQL en una unidad de todo o nada: o todos los cambios se confirman, o ninguno lo hace. El ejemplo clásico es una transferencia bancaria — debitar una cuenta y acreditar otra — donde aplicar una sin la otra crearía o haría desaparecer dinero. JDBC controla las transacciones a través del objeto Connection.

Este capítulo explica cómo abrir una transacción desactivando el auto-commit, cómo funcionan commit() y rollback(), cómo los savepoints permiten deshacer parte de una transacción, y cómo los niveles de aislamiento intercambian consistencia por concurrencia.

El auto-commit está activado por defecto

Una conexión nueva se ejecuta en modo auto-commit: cada sentencia se confirma en el instante en que termina. Eso está bien para sentencias individuales, pero no es adecuado para unidades de múltiples sentencias. Para abrir una transacción, desactiva el 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 y rollback

commit() hace permanentes y visibles para otros todos los cambios desde que comenzó la transacción. rollback() los descarta todos. La regla de oro: confirmar en caso de éxito, hacer rollback ante cualquier excepción. Olvidar el rollback deja la conexión reteniendo bloqueos y una transacción a medio terminar.

Una transferencia de ejemplo

La transferencia usa dos objetos PreparedStatement, ejecuta ambos UPDATEs y solo confirma una vez que ambos han sido ejecutados. Si alguno lanza una excepción, el bloque catch deshace todo para que no se cree ni se pierda dinero:

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

Un Savepoint es un marcador dentro de una transacción hasta el que puedes hacer rollback, deshaciendo el trabajo posterior mientras conservas el trabajo anterior. Esto es útil cuando un paso opcional dentro de una unidad mayor podría fallar pero no quieres abandonar toda la transacción:

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

Dos reglas para recordar: hacer rollback hasta un savepoint no finaliza la transacción — aún necesitas commit() o un rollback() completo después — y debes liberar los savepoints que ya no necesitas con conn.releaseSavepoint(sp) para liberar recursos de la base de datos. Los savepoints solo existen mientras el auto-commit está desactivado.

Niveles de aislamiento

setTransactionIsolation(...) intercambia consistencia por concurrencia. De más débil a más fuerte: READ_UNCOMMITTED (ve las filas "sucias" no confirmadas de otros), READ_COMMITTED (el valor predeterminado habitual), REPEATABLE_READ (las relecturas ven las mismas filas) y SERIALIZABLE (las transacciones se comportan como si se ejecutaran de una en una). Los niveles más fuertes previenen más anomalías pero permiten menos paralelismo.

Las anomalías que cada nivel previene, en orden:

  • Lectura sucia — leer una fila que otra transacción ha modificado pero no confirmado. Prevenida desde READ_COMMITTED hacia arriba.
  • Lectura no repetible — releer la misma fila y obtener un valor diferente porque otra transacción confirmó un cambio en el ínterin. Prevenida desde REPEATABLE_READ hacia arriba.
  • Lectura fantasma — volver a ejecutar la misma consulta y obtener filas adicionales porque otra transacción insertó filas coincidentes. Prevenida solo por SERIALIZABLE.

Establece el nivel antes de que comience el trabajo, y comprueba qué soporta realmente tu base de datos con DatabaseMetaData.supportsTransactionIsolationLevel(...) — no todos los drivers respetan todos los niveles.

Un ejemplo completo: niveles de aislamiento y la unidad de trabajo

Este programa imprime las cuatro constantes de nivel de aislamiento en orden de fortaleza, luego modela una transferencia como una unidad atómica — preparando dos actualizaciones y confirmando ambas o deshaciendo ambas — reproduciendo el ciclo setAutoCommit/commit/rollback sin una base de datos real.

java— editable, runs on the server

Lo que se puede extraer de la ejecución:

  • Las constantes de aislamiento aumentan con la fortaleza: READ_UNCOMMITTED (1), READ_COMMITTED (2), REPEATABLE_READ (4), SERIALIZABLE (8). Se pasa una a setTransactionIsolation para elegir cuánta anomalía de concurrencia se está dispuesto a tolerar.
  • La transferencia prepara ambas actualizaciones antes de confirmar. Esa es la esencia de una transacción: los dos UPDATEs son una unidad, y el commit() solo ocurre una vez que ambos están listos.
  • El paso commit() aquí mueve ambas sentencias de pending a committed juntas — modelando cómo la base de datos hace todos los cambios duraderos y visibles en el mismo instante, nunca a medias.
  • El bloque catch limpia pending — el sustituto de rollback(). La lección es la disciplina: cualquier excepción dentro de la transacción debe conducir a un rollback, o se deja la base de datos en un estado parcialmente aplicado.
  • transfer applied: true refleja una confirmación exitosa. Si se cambia la lógica para que la guardia falle, se verá un rollback sin nada aplicado — exactamente la garantía de todo o nada que una transacción existe para proporcionar.

Práctica

Práctica
Estableces conn.setAutoCommit(false), ejecutas dos sentencias UPDATE y la segunda lanza una SQLException. ¿Qué debe hacer tu bloque catch para preservar la integridad de los datos?
Estableces conn.setAutoCommit(false), ejecutas dos sentencias UPDATE y la segunda lanza una SQLException. ¿Qué debe hacer tu bloque catch para preservar la integridad de los datos?

Temas relacionados

  • JDBC Connection — donde viven el auto-commit, commit y rollback.
  • PreparedStatement — la forma segura de ejecutar las actualizaciones parametrizadas dentro de una transacción.
  • Batch Processing — agrupa muchas sentencias; los lotes y las transacciones suelen trabajar juntos.
  • DatabaseMetaData — consulta qué niveles de aislamiento soporta tu driver.
Was this page helpful?