4.3. Transacciones#

Hasta ahora hemos obviado el concepto de transacción. Una transacción es una operación sobre la base datos, no necesariamente atómica, que debe completarse o no hacerse en absoluto, es decir, si una transacción se compone de dos operaciones (sentencias SQL), ambas operaciones deben realizarse.

Por ejemplo, en una tienda la venta de un bolígrafo implica dos cosas:

  • Ingresar el importe del bolígrafo.

  • Eliminar el bolígrafo del almacén.

Ambas operaciones son indisolubles y hemos de hacerlas para que se complete la venta, o no hacerlas en absoluto para que quede la venta pendiente. En cambio, si se hiciera una y no la otra, la base de datos quedaría en un estado inconsistente.

4.3.1. Manejo de transacciones#

En los ejemplos con que hemos ilustrado los distintos casos, cada sentencia SQL constituye una transacción diferente. Si queremos que varias sentencias pertenezcan a una misma transacción debemos hacer lo siguiente:

Object[] registros = new Object[][] {
    new Object[] {11004866, "IES Castillo de Luna", "pública"},
    new Object[] {11007533, "IES Arroyo Hondo", "pública"},
    new Object[] {11701164, "IES Astaroth", "pública"}
};
String sqlString = "INSERT INTO Centro VALUES (?, ?, ?);"

conn.setAutoCommit(false);  // Evitamos que cada sentencia implique una transacción

try(PreparedStatement pstmt = conn.prepareStatement(sqlString)) {
    for(Object[] registro: registros) {
        pstmt.setInt(1, (int) registro[0]);
        pstmt.setString(2, (String) registro[1]);
        pstmt.setString(3, (String) registro[2]);
        pstmt.executeUpdate();
    }
    conn.commit(); // Después de ejecutar todas las sentencias, las confirmamos.
}
catch(SQLException err) {
   err.printStackTrace();
   conn.rollback();  // Hubo un problema, se deshace todo lo hecho.
}
finally {
   conn.setAutoCommit(true);  // Volvemos al comportamiento habitual.
}

Importante

En el ejemplo, las sentencias son una misma sentencia con distinto parámetros. Evidentemente, las transacciones pueden estar constituidas por cualesquiera sentencias.

Ver también

Más adelante se propone un mecanismo para gestionar transacciones con un bloque try-with-resources.

4.3.2. Operaciones masivas#

En el caso de que tengamos que llevar a cabos muchas operaciones que comparten la misma sentencia y distintos parámetros (como en el ejemplo anterior precisamente), el modo más eficiente para llevarlas a cabo es el siguiente:

Object[] registros = new Object[][] {
    new Object[] {11004866, "IES Castillo de Luna", "pública"},
    new Object[] {11007533, "IES Arroyo Hondo", "pública"},
    new Object[] {11701164, "IES Astaroth", "pública"}
};
String sqlString = "INSERT INTO Centro VALUES (?, ?, ?);"

conn.setAutoCommit(false);  // Evitamos que cada sentencia implique una transacción

try(PreparedStatement pstmt = conn.prepareStatement(sqlString)) {
    for(Object[] registro: registros) {
        pstmt.setInt(1, (int) registro[0]);
        pstmt.setString(2, (String) registro[1]);
        pstmt.setString(3, (String) registro[2]);
        pstmt.addBatch();  // Añadimos la inserción al procedimiento.
    }
    pstmt.executeBatch(); // Ejecutamos todas las inserciones pendientes.
    conn.commit(); // Confirmamos las inserciones.
}
catch(SQLException err) {
   err.printStackTrace();
   conn.rollback();  // Hubo un problema, se deshace todo lo hecho.
}
finally {
   conn.setAutoCommit(true);  // Volvemos al comportamiento habitual.
}