ResultSet
Every SELECT you run through JDBC hands back a ResultSet. It's easy to treat it like a
list of rows, but it actually behaves more like a cursor moving through the results one row at a time. Understanding
that shape avoids some of the most common JDBC beginner mistakes.
ResultSet Is a Cursor, Not a List
When a query first returns, the cursor is positioned before the first row — not on it. Calling
rs.next() moves the cursor forward one row and returns true if there was a row to move to,
or false once you've run past the last row. You must call next() before reading any column,
even if you only expect one row back.
ResultSet rs = stmt.executeQuery("SELECT id, name FROM students");
while (rs.next()) { // moves cursor forward, false when done
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.println(id + ": " + name);
}
rs.getString(...) before ever calling rs.next() throws an exception, because the cursor isn't on a row yet.The getXxx() Methods
ResultSet has a matching getter for each SQL type. Use the one that matches the column's actual type.
| Method | Used for |
|---|---|
getString(col) | VARCHAR, TEXT, CHAR columns |
getInt(col) | Whole numbers (INT) |
getDouble(col) | Decimal numbers (DOUBLE, FLOAT) |
getDate(col) | DATE columns, returned as java.sql.Date |
getBoolean(col) | BOOLEAN / TINYINT(1) columns |
while (rs.next()) {
String name = rs.getString("name");
double fees = rs.getDouble("fees");
Date joined = rs.getDate("joined_on");
boolean active = rs.getBoolean("is_active");
}
Column Name vs. Column Index
Every getter method comes in two flavors: pass a column name (String) or pass a column position
(int). Both work the same way underneath, but there's an important gotcha —
column indexes in JDBC are 1-based, not 0-based. The first column is index 1, not 0.
// by name - clearer, resists column reordering
String name = rs.getString("name");
// by index - 1-based! index 1 is the FIRST column, not 0
String sameName = rs.getString(1);
Reading by name is usually preferred because it keeps working even if the column order in the SELECT
changes. Reading by index can be marginally faster, but only if you remember that index 1 means the first
column — a mistake here is a very common source of off-by-one bugs for developers coming from 0-based
languages.
Handling SQL NULL Values Safely
A database column can hold SQL NULL, which doesn't map cleanly onto Java primitives like
int or double. If a column is NULL and you call a primitive getter like
getInt(), JDBC returns 0 instead of throwing — which is indistinguishable from an actual
stored 0 unless you check further.
Two ways to detect a real NULL safely:
// Option 1: getObject() returns a real null for NULL columns
Object value = rs.getObject("age");
if (value == null) {
System.out.println("age is NULL");
}
// Option 2: call wasNull() right after a primitive getter
int age = rs.getInt("age");
if (rs.wasNull()) {
System.out.println("age is NULL, not really 0");
}
ResultSetMetaData: Inspecting Columns Dynamically
Sometimes you don't know the shape of the query ahead of time — for example, a generic table-viewer tool that runs
arbitrary SELECT statements. ResultSetMetaData, obtained from rs.getMetaData(),
lets you inspect the column count, names, and types at runtime.
ResultSetMetaData meta = rs.getMetaData();
int columnCount = meta.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
System.out.println(meta.getColumnName(i) + " -> " + meta.getColumnTypeName(i));
}
Scrollable and Updatable ResultSets
By default, a ResultSet only moves forward with next() and can't be edited. JDBC also
supports scrollable result sets (you can move backward, jump to a row, or go back to the start) and
updatable result sets (you can edit a row and push the change back to the database). These are created by
passing extra arguments to createStatement():
Statement stmt = conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
This is a more advanced, less commonly needed feature — just know it exists for cases where forward-only, read-only reading isn't enough.