Как устроен и используется ResultSet в JDBC?

Ответ

ResultSet — это объект, представляющий результирующую таблицу данных, полученную после выполнения SQL-запроса через Statement или PreparedStatement. Он предоставляет курсор для итерации по строкам результата.

Основные характеристики и типы: При создании Statement можно задать два параметра, определяющих поведение ResultSet:

Параметр Возможные значения Описание
Тип курсора TYPE_FORWARD_ONLY (по умолчанию) Курсор движется только вперед.
TYPE_SCROLL_INSENSITIVE Курсор может двигаться в любом направлении, но не "видит" изменения, сделанные другими транзакциями после открытия.
TYPE_SCROLL_SENSITIVE Курсор может двигаться и "видит" изменения в реальном времени (редко используется, зависит от драйвера БД).
Режим конкурентности CONCUR_READ_ONLY (по умолчанию) Набор данных доступен только для чтения.
CONCUR_UPDATABLE Данные в ResultSet можно обновлять, и изменения будут синхронизированы с базой данных.

Пример использования:

// Создание прокручиваемого ResultSet только для чтения
Statement stmt = connection.createStatement(
    ResultSet.TYPE_SCROLL_INSENSITIVE,
    ResultSet.CONCUR_READ_ONLY
);
ResultSet rs = stmt.executeQuery("SELECT id, name FROM users");

// Итерация по строкам
while (rs.next()) {
    int id = rs.getInt("id"); // Получение значения по имени столбца
    String name = rs.getString(2); // Получение значения по индексу (начиная с 1)
    System.out.println(id + ": " + name);
}

// Прокрутка к первой строке и проверка пустого результата
if (rs.first()) {
    System.out.println("Первая запись: " + rs.getString("name"));
}

// Важно закрывать ресурсы
rs.close();
stmt.close();

Важно: ResultSet, Statement и Connection — это ограниченные ресурсы. Их необходимо закрывать в блоке finally или, что предпочтительнее, использовать конструкцию try-with-resources (доступна с Java 7).

Ответ 18+ 🔞

Да ты посмотри, что за зверь такой — этот ResultSet! Представь себе, сука, таблицу, которую база данных тебе выплюнула после твоего гениального SQL-запроса. Вот этот объект — он и есть та самая таблица, сволочь. А внутри у него курсор, которым ты, как указкой, по строчкам этой таблицы шаришь.

Чё он из себя вообще представляет, этот зверь? Когда ты создаёшь свой Statement, ты можешь его настроить, как тебе вздумается, двумя параметрами. А то вдруг тебе мало просто вперёд двигаться, а захочется туда-сюда поползать, как таракан по столу?

Параметр Какие бывают значения Что это значит, ёпта?
Тип курсора TYPE_FORWARD_ONLY (стоит по дефолту) Самый простой. Вперёд и только вперёд, как танк. Назад хода нет.
TYPE_SCROLL_INSENSITIVE Вот это уже веселее! Курсор может и вверх, и вниз, и на пятую строчку прыгнуть. Но он как будто снимок базы сделал и по нему ползает — новые изменения от других не видит.
TYPE_SCROLL_SENSITIVE О, это уже какой-то магический шар! Теоретически он должен изменения в реальном времени видеть. Но на практике, блядь, драйверы БД так редко это поддерживают, что проще забыть.
Режим доступа CONCUR_READ_ONLY (тоже по дефолту) Только посмотреть, потрогать нельзя. Читай и завидуй.
CONCUR_UPDATABLE А вот это уже власть! Можно прямо в ResultSet данные поменять, и они в базу уедут. Но опять же, не все базы и драйверы это любят.

Ну и как с этим чудом работать? Смотри, вот тебе кусок кода, чтоб не быть совсем уж распиздяем:

// Создаём Statement, который даст нам прокручиваемый ResultSet только для чтения
Statement stmt = connection.createStatement(
    ResultSet.TYPE_SCROLL_INSENSITIVE,
    ResultSet.CONCUR_READ_ONLY
);
ResultSet rs = stmt.executeQuery("SELECT id, name FROM users");

// И поехали гулять по строчкам, пока они не кончатся
while (rs.next()) {
    int id = rs.getInt("id"); // Достаём по имени колонки
    String name = rs.getString(2); // А можно и по индексу, только он с единицы начинается, блядь, не перепутай!
    System.out.println(id + ": " + name);
}

// А раз уж мы прокручиваемые, давай прыгнем на первую запись
if (rs.first()) {
    System.out.println("Первая запись: " + rs.getString("name"));
}

// И самое главное, ёбта! Не забудь всё закрыть, а то ресурсы кончатся, и будет тебе пиздец, а не программа.
rs.close();
stmt.close();

Внимание, важнейшая хуйня! ResultSet, Statement и Connection — это как последние сигареты в пачке, их на всех не хватит. Их обязательно нужно закрывать! Идеально — использовать try-with-resources (если ты, конечно, не на Java 6 какой-нибудь древней сидишь). Иначе твоё приложение рано или поздно накроется медным тазом с ошибкой, что соединений нет.