Transacciones en Java con commit y rollback

Resumen

Implementar transacciones en Java con MySQL te permite garantizar que un conjunto de operaciones se ejecute como una unidad atómica: o todo se guarda, o nada se guarda. Aquí verás cómo aplicarlo modificando un EmployeeRepository, controlando el autocommit y manejando commit y rollback ante excepciones.

Este recorrido es útil si trabajas con JDBC y quieres evitar inconsistencias en tu base de datos cuando varias operaciones dependen entre sí.

¿Cómo preparar la base de datos para probar transacciones?

Antes de tocar el código Java, necesitas un escenario donde una operación pueda fallar de forma controlada. La idea es agregar un campo con restricción de unicidad para forzar errores y observar el comportamiento transaccional [00:14].

Desde MySQL Workbench, abre la tabla Employees con la opción Alter Table y añade un campo nuevo:

  • Nombre del campo: curp.
  • Longitud: 18 caracteres.
  • Restricción: UNIQUE, para que no se pueda repetir entre empleados.

Esa restricción de unicidad será la que dispare la excepción más adelante y te permita ver el rollback en acción.

¿Cómo refactorizar el EmployeeRepository para una sola conexión?

Una transacción debe vivir dentro de una única conexión. Si cada método abre la suya, no hay forma de confirmarlas o revertirlas en bloque. Por eso el primer paso es centralizar la conexión en el repositorio [01:14].

En lugar del clásico private Connection getConnection(), declara una conexión como atributo y recíbela por constructor:

  • Cambia private Connection getConnection por private Connection myCon.
  • Anota la clase o el constructor con @AllArgsConstructor (o crea el constructor manualmente).
  • Reemplaza todas las llamadas a getConnection() por myCon dentro de los métodos del repositorio.

¿Por qué usar una sola Connection en una transacción? Porque commit y rollback operan sobre la conexión activa. Si abres varias, cada una maneja su propio estado y pierdes la atomicidad.

¿Qué cambios hacer en el modelo Employee?

El nuevo campo curp debe reflejarse también en la entidad. En la clase Employee agrega:

  • Atributo private String curp.
  • Getter y setter correspondientes.
  • Parámetro String curp en el constructor con this.curp = curp.
  • Línea adicional en el método toString para incluirlo.

Luego, en el EmployeeRepository, ajusta los métodos save y createEmployees para que el PreparedStatement incluya el sexto parámetro con myStmt.setString(6, employee.getCurp()), y corre el índice de los parámetros siguientes si los hay.

¿Cómo controlar autocommit, commit y rollback en el main?

Por defecto, JDBC tiene el autocommit en true, lo que significa que cada sentencia se confirma de inmediato. Para una transacción real necesitas desactivarlo y decidir tú cuándo confirmar [04:30].

En el main, abre la conexión con try-with-resources y desactiva el autocommit:

  1. Obtén la conexión: Connection myConn = DatabaseConnection.getInstance().
  2. Verifica y desactiva: if (myConn.getAutoCommit()) myConn.setAutoCommit(false);.
  3. Abre un try interno donde instancias EmployeeRepository repository = new EmployeeRepository(myConn);.
  4. Ejecuta las operaciones, por ejemplo repository.save(employee);.
  5. Confirma con myConn.commit();.
  6. En el catch, ejecuta myConn.rollback(); para revertir cualquier cambio si algo falla.

Este patrón es la base del manejo transaccional en JDBC: tú decides el punto exacto en el que los cambios se vuelven permanentes.

¿Qué hace rollback en una transacción? Devuelve la base de datos al estado en que estaba antes de iniciar la transacción, descartando todas las operaciones pendientes que aún no fueron confirmadas con commit.

¿Cómo se ve una inserción exitosa?

El primer caso de prueba inserta un empleado válido [06:20]:

  • employ.setFirstName("América").
  • employ.setSurname("López").
  • employ.setMaSurname("Villa").
  • employ.setEmail("ame@example.com").
  • employ.setSalary(300f).
  • employ.setCurp("123456789012345678") con 18 caracteres.

Al ejecutar repository.save(employ) seguido de myConn.commit(), el registro queda guardado y aparece en la tabla. Todo el flujo se completó dentro de la transacción.

¿Cómo verificar que el rollback funciona ante una excepción?

Para comprobar el comportamiento transaccional, fuerza un error usando el mismo CURP en un segundo empleado [09:10]. Como el campo es UNIQUE, MySQL lanza una excepción de entrada duplicada.

El flujo esperado es:

  • La operación entra al bloque try.
  • Al ejecutar el save, se lanza la excepción por el CURP repetido.
  • El control salta al catch, que ejecuta myConn.rollback().
  • Ningún cambio queda registrado en la base de datos.

Al revisar la tabla, no aparece ningún registro nuevo. Esa es la unidad atómica funcionando: si una parte falla, todas las operaciones del bloque se descartan, incluso aquellas que en sí mismas habrían tenido éxito.

¿Qué es una unidad atómica en bases de datos? Es un conjunto de operaciones que se ejecutan como un todo: si una falla, ninguna se aplica; si todas tienen éxito, se confirman juntas con commit.

Este patrón se vuelve crítico cuando una transacción incluye varias acciones, como insertar, actualizar y eliminar registros relacionados. Si confiaras en el autocommit, podrías terminar con datos a medias y referencias rotas entre tablas.

Cuéntame en los comentarios cómo estás implementando transacciones en tu propio proyecto y qué escenarios de rollback has tenido que resolver.