Java JDBC: Transacciones

Transacciones en Java

Una transacción es un conjunto de operaciones sobre una base de datos que se deben ejecutar como una unidad.
Hay ocasiones en las que es necesario que varias operaciones sobre la base de datos se realicen en bloque, es decir, que se ejecuten o todas o ninguna, pero no que se realicen unas sí y otras no.
Si se ejecutan parcialmente hasta que una da error, el estado de la base de datos puede quedar inconsistente. En este caso necesitaríamos un mecanismo para devolverla a su estado anterior, pudiendo deshacer todas las operaciones realizadas.
Como ejemplo, supongamos que vamos a reservar un vuelo desde Alicante hasta Sydney. Para llegar a Sydney tenemos que realizar varias escalas. Tendremos que reservar un vuelo de Alicante a Madrid, otro de Madrid a Dubai y otro desde Dubai a Sydney. Si representamos cada reserva como un insert en la base de datos tendremos las siguientes instrucciones:
try {
    .....
    s.executeUpdate("INSERT INTO RESERVAS(pasajero, origen, destino) VALUES('Felipe', 'Alicante', 'Madrid')");         
    s.executeUpdate("INSERT INTO RESERVAS(pasajero, origen, destino) VALUES ('Felipe', 'Madrid', 'Dubai')");
    s.executeUpdate("INSERT INTO RESERVAS(pasajero, origen, destino) VALUES('Felipe', 'Dubai', 'Sydney')");
    .....
} catch(SQLException e) {
    // Se ha producido un error pero no sabemos donde
    // ¿Qué operaciones se han realizado? ¿Cómo deshacerlas?
}
Un objeto Connection por defecto realiza automáticamente cada operación sobre la base de datos. Esto significa que cada vez que se ejecuta una instrucción, se refleja en la base de datos y no puede ser deshecha. Por defecto está habilitado el modo auto-commit en la conexión.
Los siguientes métodos en la interfaz Connection son utilizados para gestionar las transacciones en la base de datos:
void setAutoCommit(boolean valor)                                                                                      
void commit()
void rollback()
Para iniciar una transacción deshabilitamos el modo auto-commit mediante el método setAutoCommit(false). Esto nos da el control sobre lo que se realiza y cuándo se realiza.
Una llamada al método commit() realizará todas las instrucciones emitidas desde la última vez que se invocó el método commit().
Una llamada a rollback() deshará todos los cambios realizados desde el último commit(). Una vez se ha emitido una instrucción commit(), esas transacciones no pueden deshacerse con rollback().

try {
    .....
    conexion.setAutoCommit(false);
    s.executeUpdate("INSERT INTO RESERVAS(pasajero, origen, destino) VALUES('Felipe', 'Alicante', 'Madrid')");         
    s.executeUpdate("INSERT INTO RESERVAS(pasajero, origen, destino) VALUES ('Felipe', 'Madrid', 'Dubai')");
    s.executeUpdate("INSERT INTO RESERVAS(pasajero, origen, destino) VALUES('Felipe', 'Dubai', 'Sydney')");
    conexion.commit(); 
    .....
} catch(SQLException e) {
        if(conexion!=null){
           try {
               conexion.rollback();
           }catch (SQLException ex) {
               System.out.println(ex.toString());
           }
        }
}
Otro ejemplo de transacción: Tenemos un fragmento de código que crea un pedido, actualiza el inventario y crea un registro de envíos. Si falla la creación de un registro de envíos no se debe crear el pedido. En ese caso, los efectos de las instrucciones SQL correspondientes a las dos primeras tareas (crear un pedido y actualizar el inventario) deben deshacerse.
try {
    Class.forName("com.mysql.jdbc.Driver");
    conexion = DriverManager.getConnection("jdbc:mysql://localhost/prueba","root","1daw");
   
    conexion.setAutoCommit(false); ////// ----->> Desactivamos auto commit

    Statement st = conexion.createStatement();

    // Crear un pedido
    st.executeUpdate("INSERT INTO PEDIDOS(.......) VALUES(.....)");

    // Actualizar el inventario
    st.executeUpdate("UPDATE TABLE INVENTARIO SET stock = ...... WHERE PRODUCT ID = .....");                           

    // Crear un registro de envíos si se cumple una determinada condición
    if (Condicion que debe cumplirse) {
        st.executeUpdate("INSERT INTO ENVIOS(....) VALUES(...)");
        conexion.commit();  ///// ---->> reflejar las operaciones en la base de datos
    } else {
         conexion.rollback(); ///// -----> Deshacer operaciones
    }
} catch (SQLException e) {  //Si se produce una Excepción deshacemos las operaciones
         System.out.println(e.toString());
         if(conexion!=null){
            try {
              conexion.rollback();///// -----> Deshacer operaciones
             } catch (SQLException ex) {
                      System.out.println(ex.toString());
            }
         }
 } catch (ClassNotFoundException e) {
            System.out.println(e.toString());
 } finally {
            try {
                if (conexion != null) {
                    conexion.close();
                }
            } catch (SQLException ex) {
                System.out.println(ex.toString());
            }
 }

9 comentarios:

  1. Respuestas
    1. De nada Valentina. Gracias por el comentario.

      Eliminar
    2. aaaa pillin a las mujeres si les respondes eee

      Eliminar
    3. Y bueno... vivo el muchacho. :-)

      Eliminar
  2. Si tengo un formulario que ingresa nuevas filas en diferentes tablas esta es la mejor forma de hacerlo ?

    ResponderEliminar
    Respuestas
    1. Si, en caso de que esos ingresos a diferentes tablas estén relacionados de alguna manera.

      Eliminar
  3. y como sería el caso para una venta y el detalle_venta donde este último requiere del ID de la venta?? me darías un ejemplo? Gracias

    ResponderEliminar
  4. Un gran aporte, me gusta tu forma de explicar este tema...
    Muchas Gracias

    ResponderEliminar