Por qué son necesarias las transacciones

Muy a menudo, cuando se trabaja con una base de datos, surge una situación en la que necesita realizar muchas acciones diferentes, pero solo tienen sentido juntas.

Por ejemplo, estamos escribiendo un software bancario que debería hacer tres cosas:

  • Retirar dinero de la cuenta del cliente
  • Agregar dinero a la cuenta del destinatario
  • Registre los datos de publicación en el "registro de publicación"

Si ocurre un error durante la ejecución de alguna de estas acciones, entonces las otras dos también deben cancelarse. ¿Es imposible cancelar el dinero del cliente y no agregarlo al destinatario? Bueno, ¿o agregar al destinatario, pero no cancelar del cliente?

Entonces, tal agrupación lógica de diferentes acciones en una se llama transacción . En otras palabras, una transacción es un grupo de acciones que deben realizarse solo todas juntas . Si alguna acción falló o se ejecutó con un error, todas las demás acciones deben cancelarse.

Una transacción generalmente tiene tres estados:

  • estado inicial - el estado del sistema antes de ejecutar un grupo de acciones
  • estado de éxito: estado después de que se complete el grupo de acción
  • estado fallido - algo salió mal

En este caso, suele haber tres comandos:

  • begin/start - ejecutado antes del inicio del grupo lógico de acciones
  • commit - ejecutado después del grupo de acciones de transacción
  • revertir : inicia el proceso de devolver el sistema del estado fallido al estado inicial

Funciona así.

Primero debe abrir una transacción; llame al método begin() o start() . Llamar a este método indica el estado del sistema al que intentaremos volver si algo sale mal.

Luego se realizan todas las acciones, que se combinan en un grupo lógico: una transacción.

Luego se llama al método commit() . Su llamada marca el final de un conjunto lógico de acciones, y también suele iniciar el proceso de puesta en práctica de estas acciones.

Recuerde cómo escribimos algo en FileWriter: primero, todo lo que escribimos se almacena en la memoria y luego, cuando se llama al método flush () , todos los datos del búfer en la memoria se escriben en el disco. Este flush() es la confirmación de la transacción.

Bueno, si ocurrió un error durante la operación de la transacción, entonces debe iniciar el proceso de volver al estado inicial. Este proceso se llama rollback() , y el método del mismo nombre suele ser responsable de ello.

En términos generales, hay 2 formas de completar una transacción:

  • COMMIT - confirmamos todos los cambios realizados
  • ROLLBACK - deshace todos los cambios realizados

Transacciones en JDBC

Casi todos los DBMS pueden trabajar con transacciones. Entonces JDBC también tiene soporte para este caso. Todo se implementa de manera muy simple.

Primero, cada llamada al método execute() del objeto Statement se ejecuta en una transacción separada. Para hacer esto, Connection tiene un parámetro AutoCommit . Si se establece en true , se llamará a commit() después de cada llamada al método execute() .

En segundo lugar, si desea ejecutar varios comandos en una transacción, puede hacerlo así:

  • deshabilitar Autocompromiso
  • llamando a nuestros equipos
  • llamar al método commit() explícitamente

Parece muy simple:

connection.setAutoCommit(false);

Statement statement = connection.createStatement();
int rowsCount1 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
int rowsCount2 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
int rowsCount3 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");

connection.commit();

Si se produce un error en el servidor mientras se ejecuta el método commit() , el servidor SQL cancelará las tres acciones.

Pero hay situaciones en las que el error aún ocurre en el lado del cliente, y nunca llegamos a la llamada al método commit() :

connection.setAutoCommit(false);

Statement statement = connection.createStatement();
int rowsCount1 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
int rowsCount2 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
int rowsCount3 = statement.executeUpdate("UPDATE multiple typos will result in an exception");

connection.commit();

Si ocurre un error durante la ejecución de una executeUpdate() , entonces no se llamará al método commit() . Para revertir todas las acciones realizadas, debe llamar al método rollback() . Por lo general, se ve así:

try{
  	connection.setAutoCommit(false);

  	Statement statement = connection.createStatement();
  	int rowsCount1 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
  	int rowsCount2 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
  	int rowsCount3 = statement.executeUpdate("UPDATE multiple typos will result in an exception");

	  connection.commit();
 }
 catch (Exception e) {
   connection.rollback();
}

Puntos de guardado

Con la llegada de JDBC 3.0, se hizo posible trabajar de manera más eficiente con la reversión de transacciones. Ahora puede establecer puntos de guardado: puntos de guardado, y cuando llame a la operación de reversión () , retroceda a un punto de guardado específico.

Para guardar, debe crear un punto de guardado, esto se hace con el comando:

Savepoint save = connection.setSavepoint();

La reversión a un punto de guardado se realiza con el comando:

connection.rollback(save);

Intentemos agregar un punto de guardado antes de nuestro comando problemático:

try{
  	connection.setAutoCommit(false);

  	Statement statement = connection.createStatement();
  	int rowsCount1 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
  	int rowsCount2 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");

  	Savepoint save = connection.setSavepoint();
 	 try{
      	int rowsCount3 = statement.executeUpdate("UPDATE multiple typos will result in an exception");
 	 }
 	 catch (Exception e) {
    	   connection.rollback(save);
 	 }

	  connection.commit();
 }
 catch (Exception e) {
   connection.rollback();
}

Organizamos transacciones anidadas agregando un punto de guardado antes de llamar al método problemático y volviendo al estado guardado llamando al método rollback (guardar) .

Sí, es muy similar a guardar/cargar en los juegos.