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.
Qué extraer de la ejecución:
- El cursor comienza en el índice
-1— antes de la primera fila — y el paso++cursorimita ars.next(): avanza primero, luego lee. Esto es exactamente por qué un bucle real eswhile (rs.next())y nunca lee una columna antes del primernext(). - Las filas se leen de una en una, no se cargan como lista. El modelo usa una lista por simplicidad, pero un
ResultSetreal transmite filas desde el servidor, lo que le permite manejar conjuntos de resultados mucho mayores que la memoria disponible. - La puntuación
nullde Linus se imprimió como0— la misma trampa que ponegetInt. 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 ars.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.