9.4 Navegabilidad y concurrencia

Cuando invocamos a createStatement() sin argumentos, como hemos visto anteriormente, al ejecutar sentencias SQL obtendremos un ResultSet por defecto en el que el cursor solo puede moverse hacia adelante y los datos son de solo lectura. A veces esto no es suficiente y necesitamos mayor funcionalidad.

Por ello el método createStatement() está sobrecargado (existen varias versiones de dicho método), lo cual nos permite invocarlo con argumentos en los que podemos especificar el funcionamiento.

  • Statement createStatement (int resultSetType, int resultSetConcurrency): devuelve un objeto Statement cuyos objetos ResultSet serán del tipo y concurrencia especificados. Los valores válidos son constantes definidas en ResultSet.

Valores válidos para el argumento resultSetType (indica el tipo de ResultSet):

  • ResultSet.TYPE_FORWARD_ONLY: ResultSet por defecto, forward-only y no-actualizable.
    • Solo permite movimiento hacia delante con next().
    • Sus datos NO se actualizan. Es decir, no reflejará cambios producidos en la base de datos. Contiene una instantánea del momento en el que se realizó la consulta.
  • ResultSet.TYPE_SCROLL_INSENSITIVE: ResultSet desplazable y no-actualizable.
    • Permite libertad de movimiento del cursor con otros métodos como first(), previous(), last(), etc. además de next().
    • Sus datos NO se actualizan, como en el caso anterior.
  • ResultSet.TYPE_SCROLL_SENSITIVE: ResultSet desplazable y actualizable.
    • Permite libertad de movimientos del cursor, como en el caso anterior.
    • Sus datos SÍ se actualizan. Es decir, mientras el ResultSet esté abierto se actualizará automáticamente con los cambios producidos en la base de datos. Esto puede suceder incluso mientras se está recorriendo el ResultSet, lo cual puede ser conveniente o contraproducente según el caso.

Diferencia entre ResultSet.TYPE_SCROLL_INSENSITIVE y ResultSet.TYPE_SCROLL_SENSITIVE

import java.sql.*;

public class EjemploScrollInsensitive {
    private static final String JDBC_URL = "jdbc:mysql://localhost:3306/pr_tuNombre";
    private static final String USUARIO = "pr_tuNombre";
    private static final String PASSWD = "1234";   

    public static void main(String[] args) {
        try (Connection con = DriverManager.getConnection(JDBC_URL, USUARIO, PASSWD);
            Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
            ResultSet rs = stmt.executeQuery("SELECT id, nombre FROM usuarios")) {

            // Mover a la primera fila
            if (rs.first()) {
                System.out.println("primera fila: " + rs.getInt("id") + ", " + rs.getString("nombre"));
            }

            // Mover a la última fila
            if (rs.last()) {
                System.out.println("última fila: " + rs.getInt("id") + ", " + rs.getString("nombre"));
            }

            // Simulamos un retraso y actualizamos la base de datos (en otra sesión)
            System.out.println("Esperando las actualizaciones...");
            Thread.sleep(10000); // Esperar 10 segundos

            // Mover a la primera fila otra vez
            if (rs.first()) {
                System.out.println("primera fila después de esperar: " + rs.getInt("id") + ", " + rs.getString("nombre"));
            }

        } catch (SQLException | InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}

En este ejemplo, incluso si la base de datos cambia mientras el programa está esperando (durante el Thread.sleep(10000)), el ResultSet no reflejará esos cambios cuando se vuelva a consultar la primera fila.

En caso de cambiar el valor ResultSet.TYPE_SCROLL_INSENSITIVE por ResultSet.TYPE_SCROLL_SENSITIVE, si hay cambios en la base de datos durante el tiempo de espera, el ResultSet reflejará esos cambios cuando se vuelva a consultar la primera fila. Por ejemplo, si se actualiza el nombre del primer registro en la base de datos mientras el programa espera, el nuevo nombre aparecerá en la salida.

Estos ejemplos demuestran cómo ResultSet.TYPE_SCROLL_INSENSITIVE no refleja cambios en la base de datos después de su creación, mientras que ResultSet.TYPE_SCROLL_SENSITIVE sí lo hace.


Valores válidos para el argumento resultSet.Concurrency (indica la concurrencia del ResultSet):

  • ResultSet.CONCUR_READ_ONLY: solo lectura. Es el valor por defecto.
  • ResultSet.CONCUR_UPDATABLE: permite modificar los datos almacenados en el ResultSet para luego aplicar los cambios sobre la base de datos (puedes insertar, actualizar y borrar filas). Más adelante se verá cómo).

A tener en cuenta

El ResultSet por defecto que se obtiene con createStatement() sin argumentos es el mismo que con createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY).