W3docs

Java JDBC ResultSet

Itera y lee filas de una consulta SQL en Java con la interfaz ResultSet.

Un ResultSet es un cursor sobre las filas que devolvió una consulta. No almacena todas las filas en memoria a la vez; apunta a una fila a la vez y tú la recorres hacia adelante. Es fundamental saber que un ResultSet recién creado está posicionado antes de la primera fila — debes llamar a next() una vez para llegar a la primera. Por eso cada bucle de lectura es while (rs.next()).

Obtienes un ResultSet como resultado de una consulta ejecutada a través de un Statement o un PreparedStatement. Esta página explica cómo recorrer ese cursor y leer columnas de forma segura.

El bucle de lectura

String sql = "SELECT id, name, score FROM player ORDER BY score DESC";
try (Statement st = conn.createStatement();
     ResultSet rs = st.executeQuery(sql)) {
  while (rs.next()) {                       // advance; false when no more rows
    int id = rs.getInt("id");               // read by column name...
    String name = rs.getString(2);          // ...or by 1-based index
    int score = rs.getInt("score");
    System.out.println(id + " " + name + " " + score);
  }
}

Lectura de columnas: por nombre o por índice

Ambas opciones funcionan. Los nombres de columna son más claros y sobreviven a un SELECT reordenado; los índices de columna (basados en 1, como todo en JDBC) son marginalmente más rápidos. Los nombres ganan en mantenibilidad en casi todo el código. Si necesitas los nombres, tipos o el número de columnas en tiempo de ejecución — por ejemplo para renderizar una tabla genérica — léelos desde el ResultSetMetaData que obtienes con rs.getMetaData().

El problema de NULL y wasNull()

Los getters primitivos no pueden devolver null. getInt retorna 0 ante un NULL SQL, getDouble retorna 0.0, y así sucesivamente — indistinguible de un cero real. Cuando una columna es nullable y la diferencia importa, llama a rs.wasNull() inmediatamente después del getter:

int score = rs.getInt("score");
if (rs.wasNull()) { /* it was SQL NULL, not a zero */ }

(Para tipos objeto, getObject devuelve null directamente, lo que suele ser más limpio.)

Tipo de cursor y concurrencia

Por defecto un ResultSet es TYPE_FORWARD_ONLY y CONCUR_READ_ONLY — avanzas hacia adelante y solo lees. Solicita TYPE_SCROLL_INSENSITIVE para poder llamar a previous(), absolute(n) o first(), o CONCUR_UPDATABLE para editar filas en el lugar. Estos tienen mayor costo, así que solicítalos solo cuando sean necesarios.

Ciérralo (try-with-resources lo hace)

Un ResultSet mantiene un cursor del lado del servidor; dejarlo abierto desperdicia recursos de la base de datos. Cerrar su Statement lo cierra, y try-with-resources se encarga de ambos. Nunca devuelvas un ResultSet abierto desde un método cuya Connection ya se haya cerrado.

Un ejemplo completo: el cursor forward-only y wasNull

Este programa modela el cursor de ResultSet con una pequeña lista en memoria — comenzando antes de la primera fila, avanzando con un paso al estilo next() — y reproduce el comportamiento de getInt que devuelve 0 para NULL mediante una comprobación con wasNull, junto con las constantes del cursor.

java— editable, runs on the server

Qué extraer de la ejecución:

  • El cursor comienza en el índice -1antes de la primera fila — y el paso ++cursor imita a rs.next(): avanza primero, luego lee. Esto es exactamente por qué un bucle real es while (rs.next()) y nunca lee una columna antes del primer next().
  • Las filas se leen de una en una, no se cargan como lista. El modelo usa una lista por simplicidad, pero un ResultSet real transmite filas desde el servidor, lo que le permite manejar conjuntos de resultados mucho mayores que la memoria disponible.
  • La puntuación null de Linus se imprimió como 0 — la misma trampa que pone getInt. Sin la bandera no podrías distinguir una puntuación ausente de un cero genuino.
  • El marcador (wasNull) es el disambiguador. En JDBC real llamas a rs.wasNull() justo después del getter, porque reporta sobre la columna leída más recientemente — lee otra columna primero y la respuesta cambia.
  • Las constantes (TYPE_FORWARD_ONLY, CONCUR_READ_ONLY, FETCH_FORWARD) describen el cursor predeterminado y más económico: avanza hacia adelante, solo lectura. Los cursores desplazables o actualizables se solicitan deliberadamente, porque exigen más trabajo al servidor.

Práctica

Práctica
Una consulta lee una columna 'score' nullable con rs.getInt('score') y obtiene 0. ¿Cómo distingues si el valor era un 0 real o un NULL SQL?
Una consulta lee una columna 'score' nullable con rs.getInt('score') y obtiene 0. ¿Cómo distingues si el valor era un 0 real o un NULL SQL?
Was this page helpful?