Acceso a bases de datos con JDBC: tutorial completo Java 21

Por defecto, JDBC ejecuta cada sentencia en modo auto-commit: cada operación se confirma automáticamente. Para operaciones que deben ser atómicas (o todas tienen éxito o ninguna), es obligatorio gestionar la transacción manualmente.

Transacción básica

try (Connection con = DatabaseConfig.getConnection()) {
    con.setAutoCommit(false); // iniciar transacción

    try {
        // Operación 1: debitar cuenta origen
        try (PreparedStatement ps = con.prepareStatement(
                "UPDATE cuentas SET saldo = saldo - ? WHERE id = ?")) {
            ps.setDouble(1, 500.0);
            ps.setInt(2, 1001);
            ps.executeUpdate();
        }

        // Operación 2: acreditar cuenta destino
        try (PreparedStatement ps = con.prepareStatement(
                "UPDATE cuentas SET saldo = saldo + ? WHERE id = ?")) {
            ps.setDouble(1, 500.0);
            ps.setInt(2, 1002);
            ps.executeUpdate();
        }

        // Registrar la transferencia
        try (PreparedStatement ps = con.prepareStatement(
                "INSERT INTO transferencias (origen, destino, importe, fecha) VALUES (?, ?, ?, NOW())")) {
            ps.setInt(1, 1001); ps.setInt(2, 1002); ps.setDouble(3, 500.0);
            ps.executeUpdate();
        }

        con.commit(); // todo correcto: confirmar
        System.out.println("Transferencia completada");

    } catch (SQLException e) {
        con.rollback(); // algo falló: deshacer todo
        System.err.println("Transacción revertida: " + e.getMessage());
        throw e;
    } finally {
        con.setAutoCommit(true); // restaurar (obligatorio si se devuelve al pool)
    }
}

Niveles de aislamiento

// READ COMMITTED (recomendado para la mayoría de aplicaciones)
con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

// SERIALIZABLE (máxima consistencia, menor concurrencia)
con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);

// REPEATABLE READ (por defecto en MySQL InnoDB)
con.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);

Savepoints

con.setAutoCommit(false);
Savepoint sp = con.setSavepoint("inicio_lote");
try {
    // Operaciones del lote...
    con.commit();
} catch (SQLException e) {
    con.rollback(sp); // revertir solo hasta el savepoint
    // Continuar con el lote parcial...
    con.commit();
}

Batch: inserciones masivas

Para insertar o actualizar miles de filas, el modo batch reduce drasticamente el número de roundtrips a la base de datos:

String sql = "INSERT INTO lecturas (sensor_id, valor, timestamp) VALUES (?, ?, ?)";

try (Connection con = DatabaseConfig.getConnection();
     PreparedStatement ps = con.prepareStatement(sql)) {

    con.setAutoCommit(false);
    int BATCH_SIZE = 1000;
    int contador = 0;

    for (Lectura l : lecturas) {
        ps.setInt(1, l.sensorId());
        ps.setDouble(2, l.valor());
        ps.setTimestamp(3, Timestamp.from(l.timestamp()));
        ps.addBatch();

        if (++contador % BATCH_SIZE == 0) {
            ps.executeBatch();
            con.commit();
        }
    }
    ps.executeBatch(); // flush del último lote parcial
    con.commit();
    System.out.println("Insertados: " + contador);
}

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR