ResultSet in Java
A ResultSet is the object that holds the rows returned by a query. It works with a cursor that points at one row at a time. The cursor starts before the first row; each call to next() moves it forward one row and returns false when there are no more. While the cursor is on a row, getter methods such as getInt and getString read that row's columns. By default a result set can only move forward and is read-only, but you can ask for one that scrolls in any direction or even updates the table. In this lesson, you will learn the cursor, the getters, how to deal with SQL NULL, and the different types of result set.
Methods for Moving the Cursor
| Method | Description | Needs a scrollable result set? |
|---|---|---|
next() | It is used to move to the next row. Returns false after the last row. | No |
previous() | It moves back one row. | Yes |
first(), last() | They jump to the first or last row. | Yes |
absolute(n) | It jumps to row n (1 is the first). | Yes |
relative(n) | It moves n rows forward, or backward if negative. | Yes |
beforeFirst(), afterLast() | They move outside the rows. | Yes |
getRow() | It returns the current row number. | No |
Methods for Reading Columns
Every getter comes in two forms: by column label, getInt("marks"), or by position, getInt(3), counting from 1.
| SQL type | Getter | Java type |
|---|---|---|
INT, SMALLINT | getInt | int |
BIGINT | getLong | long |
DECIMAL, NUMERIC | getBigDecimal | BigDecimal |
DOUBLE, FLOAT | getDouble | double |
VARCHAR, CHAR, TEXT | getString | String |
BOOLEAN, BIT | getBoolean | boolean |
DATE, TIME, TIMESTAMP | getObject(col, LocalDate.class) and so on | LocalDate, LocalTime, LocalDateTime |
BLOB, CLOB | getBinaryStream, getCharacterStream | Streams |
| Anything | getObject | Object |
Types of ResultSet
You choose the type when creating the statement: con.createStatement(type, concurrency).
| Type constant | Cursor movement | Sees changes made by others? |
|---|---|---|
TYPE_FORWARD_ONLY (default) | Forward only | Not applicable |
TYPE_SCROLL_INSENSITIVE | Any direction | No |
TYPE_SCROLL_SENSITIVE | Any direction | Yes, if the driver supports it |
| Concurrency constant | Meaning |
|---|---|
CONCUR_READ_ONLY (default) | Rows can only be read |
CONCUR_UPDATABLE | Rows can be changed through the result set |
Example: Reading, Scrolling and Updating
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class Results {
public static void main(String[] args) throws Exception {
try (Connection con =
DriverManager.getConnection("jdbc:h2:mem:school")) {
try (Statement st = con.createStatement()) {
st.execute("CREATE TABLE student (id INT PRIMARY KEY, "
+ "name VARCHAR(50), marks INT, city VARCHAR(30))");
st.executeUpdate("INSERT INTO student VALUES "
+ "(1, 'Navika', 91, 'Plano'), "
+ "(2, 'Srihitha', NULL, 'Tirupati'), "
+ "(3, 'Advitha', 78, NULL)");
}
// 1. The default ResultSet: forward only, read only.
try (Statement st = con.createStatement();
ResultSet rs = st.executeQuery(
"SELECT id, name, marks, city FROM student ORDER BY id")) {
while (rs.next()) {
int marks = rs.getInt("marks");
String shown = rs.wasNull() ? "no marks" : marks + "";
System.out.println(rs.getInt(1) + " " + rs.getString("name")
+ " | " + shown + " | " + rs.getString("city"));
}
}
// 2. A scrollable ResultSet can move in any direction.
try (Statement st = con.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ResultSet rs = st.executeQuery(
"SELECT name FROM student ORDER BY id")) {
rs.last();
System.out.println("last: " + rs.getString(1)
+ " (row " + rs.getRow() + ")");
rs.first();
System.out.println("first: " + rs.getString(1));
rs.absolute(2);
System.out.println("row 2: " + rs.getString(1));
rs.previous();
System.out.println("back: " + rs.getString(1));
}
// 3. An updatable ResultSet can change the table.
try (Statement st = con.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = st.executeQuery(
"SELECT id, name, marks, city FROM student ORDER BY id")) {
rs.absolute(2);
rs.updateInt("marks", 84);
rs.updateRow();
}
try (Statement st = con.createStatement();
ResultSet rs = st.executeQuery(
"SELECT marks FROM student WHERE id = 2")) {
rs.next();
System.out.println("Srihitha now has " + rs.getInt(1));
}
}
}
}1 Navika | 91 | Plano
2 Srihitha | no marks | Tirupati
3 Advitha | 78 | null
last: Advitha (row 3)
first: Navika
row 2: Srihitha
back: Navika
Srihitha now has 84Explanation:
- The loop
while (rs.next())is the standard way to read every row. Columns are read by position (getInt(1)) or by label (getString("name")). NULLand primitives: Srihitha's marks are SQLNULL.getIntcan't returnnull, so it returns 0. Callingrs.wasNull()right after the getter tells you the value was reallyNULL. For Advitha's city,getStringsimply returns Javanull.- The scrollable result set jumps to the last row, the first, row 2, and back one.
- The updatable result set moves to row 2, changes
markswithupdateInt, and saves it withupdateRow(). The last query confirms the table was changed.
Reading NULL Safely
| Approach | Example |
|---|---|
Check wasNull() after the getter | int m = rs.getInt("marks"); if (rs.wasNull()) { ... } |
| Read as a wrapper object | Integer m = rs.getObject("marks", Integer.class); returns null for NULL |
By Label or by Position?
By label: getString("name") | By position: getString(2) | |
|---|---|---|
| Readability | Clear | You must count columns |
If the SELECT list changes | Still correct | Silently reads the wrong column |
| Speed | A tiny lookup cost | Fastest |
Prefer labels. And list the columns you need in the SELECT, instead of SELECT *, so the result doesn't change when the table does.
Common Mistakes
Reading before calling next(). The cursor isn't on a row yet; the getter throws SQLException.
Using index 0. Columns start at 1.
Trusting getInt for nullable columns. NULL reads as 0.
Calling previous() on a default result set. A forward-only result set throws; ask for a scrollable one.
Returning a ResultSet from a method after closing its connection. It can no longer be read.
Interview Corner
What is a ResultSet and what types are there?
A ResultSet represents the rows returned by a query and maintains a cursor to the current row, initially positioned before the first row. By type: TYPE_FORWARD_ONLY (default, moves only forward), TYPE_SCROLL_INSENSITIVE (scrolls both ways, doesn't reflect later changes by others) and TYPE_SCROLL_SENSITIVE (scrolls and may reflect changes). By concurrency: CONCUR_READ_ONLY (default) and CONCUR_UPDATABLE, which allows updateXxx, updateRow, insertRow and deleteRow.
How do you handle NULL values in a ResultSet?
Getters for primitive types return a default for SQL NULL (0 or false), so call wasNull() immediately after the getter to check whether the last column read was NULL. Alternatively read the column as an object, for example getObject("marks", Integer.class), which returns Java null.
Practice
- Easy: Print the students in reverse order using a scrollable result set,
afterLast()andprevious(). - Harder: With an updatable result set, add a new student using
moveToInsertRow(), theupdateXxxmethods andinsertRow().
Key Takeaways
Was this tutorial helpful?