Processing ResultSets
Processing ResultSets
A ResultSet is the object JDBC hands back after a SELECT query. Think of it as a scrollable table: it has a cursor that starts before the first row, and you advance it row by row (or jump around, if your driver supports scrollable sets). Understanding how to move through rows, extract typed values, and inspect the schema at runtime makes you efficient with JDBC and prepares you for the edge cases production code always surfaces.
The Cursor Model
When executeQuery() returns, the cursor sits before row 1. You must call next() to move to the first row. next() returns true as long as a row was reached, and false when the data is exhausted — which makes it a natural while loop condition:
ResultSet keeps a server-side cursor open, consuming memory on both client and database.
Typed Getter Methods
JDBC provides two variants of every getter: one that takes the column label (a String) and one that takes the column index (a 1-based int). Column labels are more readable and survive SELECT * column-order changes; column indexes are marginally faster in tight loops.
The most common typed getters:
getString(col)— maps toVARCHAR,TEXT,CHARgetLong(col)/getInt(col)— maps toBIGINT/INTgetDouble(col)/getBigDecimal(col)— floating-point and exact decimalgetBoolean(col)— maps toBOOLEAN/TINYINT(1)getTimestamp(col)— maps toDATETIME/TIMESTAMPgetDate(col)— maps toDATE(time portion is midnight)getBytes(col)— binary data (BLOB)getObject(col)— driver-chosen Java type; useful when the type is unknown at compile time
BigDecimal for monetary values. getDouble introduces floating-point rounding errors. For financial or scientific columns, always use getBigDecimal.
Handling SQL NULL
SQL NULL is not the same as Java null. When you call a primitive getter like getLong() on a NULL column, JDBC returns 0 — not an exception. To distinguish a true zero from a missing value, call wasNull() immediately after the getter:
For Object getters (getString, getBigDecimal, getTimestamp) JDBC returns a proper Java null, so wasNull() is only necessary for primitive getters.
wasNull() before calling a getter. It reflects the status of the last column read, not any future one. The order of calls matters.
ResultSetMetaData: Inspecting Columns at Runtime
ResultSetMetaData describes the shape of the result — column count, names, types, nullability, display widths. This is indispensable when you are writing generic code (a CSV exporter, a data-diff tool, a test fixture printer) that must handle arbitrary queries:
Key ResultSetMetaData methods:
getColumnCount()— how many columns in the resultgetColumnLabel(i)— the alias (ASlabel) or original namegetColumnName(i)— the original table column name (differs from label when aliased)getColumnTypeName(i)— database-specific type string (e.g.VARCHAR)getColumnType(i)—java.sql.Typesconstant (e.g.Types.VARCHAR == 12)isNullable(i)—columnNoNulls,columnNullable, orcolumnNullableUnknowngetColumnDisplaySize(i)— max characters for display
Building a Generic Row Mapper
A common pattern is to abstract result-to-object mapping with a functional interface, which fits naturally with lambdas you already know:
RowMapper<T> exists and what happens under the hood in every JDBC abstraction layer.
Scrollable and Updatable ResultSets
By default, a ResultSet is forward-only and read-only. If you need to move backwards or update rows in place, pass extra constants when creating the statement:
Scroll types: TYPE_FORWARD_ONLY (default, most efficient), TYPE_SCROLL_INSENSITIVE (snapshot, does not see concurrent changes), TYPE_SCROLL_SENSITIVE (live view, driver-dependent and rarely used).
UPDATE statement for clarity and portability.
Summary
A ResultSet cursor starts before row 1; advance it with next() in a while loop. Use typed getters by column label for clarity, BigDecimal for money, and wasNull() after primitive getters to distinguish zero from NULL. ResultSetMetaData gives you the column schema at runtime and powers generic mappers. Wrapping mapping logic in a RowMapper<T> functional interface is clean, testable, and the foundation of higher-level frameworks.