← Назад к вопросам

Назови особенности конфигурации ResultSet в JDBC

2.2 Middle🔥 121 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Особенности конфигурации ResultSet в JDBC

ResultSet - это объект в JDBC, который содержит результаты SQL запроса. Его можно конфигурировать разными способами для оптимизации работы с данными.

Основной процесс

Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");

while (rs.next()) {
    int id = rs.getInt("id");
    String name = rs.getString("name");
    System.out.println(id + ": " + name);
}

1. Тип ResultSet (Type)

Первый параметр конфигурации - это тип ResultSet, который определяет как можно перемещаться по результатам.

TYPE_FORWARD_ONLY (по умолчанию):

  • Можно только вперёд через результаты
  • Быстрее всего
  • Используется, когда данные обрабатываются один раз
Statement stmt = conn.createStatement(
    ResultSet.TYPE_FORWARD_ONLY,
    ResultSet.CONCUR_READ_ONLY
);
ResultSet rs = stmt.executeQuery("SELECT * FROM users");

while (rs.next()) {
    // Можно только идти вперёд
    System.out.println(rs.getString("name"));
}
// rs.previous();  // ОШИБКА - нельзя вернуться назад

TYPE_SCROLL_INSENSITIVE:

  • Можно двигаться в обе стороны (вперёд и назад)
  • Менее чувствительный к изменениям в БД после создания ResultSet
Statement stmt = conn.createStatement(
    ResultSet.TYPE_SCROLL_INSENSITIVE,
    ResultSet.CONCUR_READ_ONLY
);
ResultSet rs = stmt.executeQuery("SELECT * FROM users");

rs.last();           // Перейти в конец
rs.beforeFirst();    // Перейти перед первой строкой
rs.absolute(5);      // Перейти на 5-ю строку
rs.relative(-2);     // Переместиться на 2 строки назад
rs.previous();       // Перейти на предыдущую строку

TYPE_SCROLL_SENSITIVE:

  • Чувствителен к изменениям в БД
  • Отражает обновления, вставки и удаления
  • Самый медленный
Statement stmt = conn.createStatement(
    ResultSet.TYPE_SCROLL_SENSITIVE,
    ResultSet.CONCUR_UPDATABLE
);
ResultSet rs = stmt.executeQuery("SELECT * FROM users");

// ResultSet будет отражать изменения, сделанные другими сессиями
while (rs.next()) {
    System.out.println(rs.getString("name"));
}

2. Свойство Concurrency (Конкурентность)

Второй параметр определяет, можно ли изменять данные через ResultSet.

CONCUR_READ_ONLY (по умолчанию):

  • Только чтение
  • Быстрее
  • Минимум ресурсов
Statement stmt = conn.createStatement(
    ResultSet.TYPE_FORWARD_ONLY,
    ResultSet.CONCUR_READ_ONLY
);
ResultSet rs = stmt.executeQuery("SELECT * FROM users");

rs.next();
rs.updateString("name", "John");  // ОШИБКА - читаем только

CONCUR_UPDATABLE:

  • Можно обновлять данные через ResultSet
  • Медленнее
  • Требует SELECT ввода PRIMARY KEY
Statement stmt = conn.createStatement(
    ResultSet.TYPE_SCROLL_SENSITIVE,
    ResultSet.CONCUR_UPDATABLE
);
ResultSet rs = stmt.executeQuery("SELECT id, name, email FROM users");

while (rs.next()) {
    String currentName = rs.getString("name");
    
    if (currentName.equals("OldName")) {
        // Обновляем данные прямо через ResultSet
        rs.updateString("name", "NewName");
        rs.updateRow();  // Фиксируем изменения в БД
    }
}

3. Holdability (Переносимость)

Определяет, что происходит с ResultSet при commit транзакции.

HOLD_CURSORS_OVER_COMMIT (по умолчанию):

  • ResultSet остаётся открытым после commit
Connection conn = DriverManager.getConnection(url);
Statement stmt = conn.createStatement(
    ResultSet.TYPE_FORWARD_ONLY,
    ResultSet.CONCUR_READ_ONLY,
    ResultSet.HOLD_CURSORS_OVER_COMMIT
);
ResultSet rs = stmt.executeQuery("SELECT * FROM users");

conn.commit();  // ResultSet остаётся открытым
while (rs.next()) {
    System.out.println(rs.getString("name"));
}

CLOSE_CURSORS_AT_COMMIT:

  • ResultSet закрывается при commit
  • Экономит ресурсы
Statement stmt = conn.createStatement(
    ResultSet.TYPE_FORWARD_ONLY,
    ResultSet.CONCUR_READ_ONLY,
    ResultSet.CLOSE_CURSORS_AT_COMMIT
);
ResultSet rs = stmt.executeQuery("SELECT * FROM users");

conn.commit();  // ResultSet закроется
// rs.next();   // ОШИБКА - ResultSet уже закрыт

4. Fetch Size (Размер выборки)

Определяет, сколько строк скачивать за один раз из БД.

По умолчанию - зависит от драйвера (обычно 10-100 строк)

Statement stmt = conn.createStatement();
stmt.setFetchSize(1000);  // Скачивать по 1000 строк за раз
ResultSet rs = stmt.executeQuery("SELECT * FROM large_table");

while (rs.next()) {
    System.out.println(rs.getString("name"));
}

Оптимизация:

  • Если много данных - увеличивай fetch size (экономит сетевые запросы)
  • Если мало памяти - уменьшай fetch size
  • Для больших таблиц: 1000-5000 оптимально

5. Fetch Direction (Направление выборки)

Указывает, в каком направлении обрабатывать строки.

Statement stmt = conn.createStatement();
stmt.setFetchDirection(ResultSet.FETCH_FORWARD);   // Вперёд (по умолчанию)
stmt.setFetchDirection(ResultSet.FETCH_BACKWARD);  // Назад
stmt.setFetchDirection(ResultSet.FETCH_UNKNOWN);   // Неизвестно

ResultSet rs = stmt.executeQuery("SELECT * FROM users");

Практический пример

public class JDBCExample {
    public static void processLargeTable(Connection conn) throws SQLException {
        // Конфигурируем для больших объёмов данных
        Statement stmt = conn.createStatement(
            ResultSet.TYPE_FORWARD_ONLY,      // Только вперёд - быстро
            ResultSet.CONCUR_READ_ONLY,       // Только чтение
            ResultSet.CLOSE_CURSORS_AT_COMMIT // Закрывать при commit
        );
        
        stmt.setFetchSize(5000);              // Скачивать большими порциями
        
        try (ResultSet rs = stmt.executeQuery(
            "SELECT id, name, email FROM users WHERE active = true"
        )) {
            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String email = rs.getString("email");
                
                processRow(id, name, email);
            }
        }
    }
    
    public static void editableResultSet(Connection conn) throws SQLException {
        // Конфигурируем для обновления данных
        Statement stmt = conn.createStatement(
            ResultSet.TYPE_SCROLL_SENSITIVE,  // Чувствителен к изменениям
            ResultSet.CONCUR_UPDATABLE        // Можно обновлять
        );
        
        try (ResultSet rs = stmt.executeQuery(
            "SELECT id, name, email FROM users WHERE id = ?"
        )) {
            if (rs.next()) {
                rs.updateString("email", "newemail@example.com");
                rs.updateRow();  // Отправить изменения в БД
            }
        }
    }
}

Best Practices

  1. Используй TYPE_FORWARD_ONLY по умолчанию - это самый быстрый вариант
  2. Всегда используй CONCUR_READ_ONLY если не нужно обновлять
  3. Устанавливай fetchSize в зависимости от объёма данных - это критично для производительности
  4. Закрывай ResultSet явно или используй try-with-resources
  5. Для больших таблиц - TYPE_FORWARD_ONLY + CONCUR_READ_ONLY + большой fetchSize

Современный подход

Сегодня вместо JDBC часто используют:

  • JPA/Hibernate - ORM упрощает работу с БД
  • Spring Data JPA - ещё более высокоуровневая абстракция
  • Reactive drivers (R2DBC) - асинхронная работа с БД

Но понимание JDBC остаётся важным для подготовки к собеседованиям и написания оптимального SQL кода.

Назови особенности конфигурации ResultSet в JDBC | PrepBro