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

Можно ли поменять объект ResultSet и сразу же добавить его в базу данных?

1.8 Middle🔥 81 комментариев
#ORM и Hibernate

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

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

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

Изменение ResultSet и сохранение в БД

Ответ: частично да, но это очень редко используется и имеет множество ограничений. Разберёмся в деталях.

Что такое ResultSet

ResultSet — это объект, который представляет результаты SQL запроса. Обычно это read-only (только для чтения):

Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT id, name FROM users");

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

Updatable ResultSet

Докер предоставляет специальный режим Updatable ResultSet, который позволяет:

  • Изменять значения
  • Вставлять новые строки
  • Удалять существующие строки

Создание Updatable ResultSet:

// Обычный read-only ResultSet
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT id, name FROM users");

// Updatable ResultSet
Statement updateStmt = connection.createStatement(
    ResultSet.TYPE_SCROLL_SENSITIVE,      // Может скролиться и видит обновления
    ResultSet.CONCUR_UPDATABLE            // Можно редактировать!
);
ResultSet updatableRs = updateStmt.executeQuery("SELECT id, name FROM users");

Типы ResultSet:

// TYPE (движение по результатам)
ResultSet.TYPE_FORWARD_ONLY       // Только вперед (default)
ResultSet.TYPE_SCROLL_INSENSITIVE // Может прыгать туда-сюда, но не видит изменения
ResultSet.TYPE_SCROLL_SENSITIVE   // Может прыгать и видит изменения

// CONCUR (параллелизм/редактирование)
ResultSet.CONCUR_READ_ONLY        // Только чтение (default)
ResultSet.CONCUR_UPDATABLE        // Можно менять!

Как изменять и сохранять ResultSet

Пример 1: Обновление существующей строки

Statement stmt = connection.createStatement(
    ResultSet.TYPE_SCROLL_SENSITIVE,
    ResultSet.CONCUR_UPDATABLE
);
ResultSet rs = stmt.executeQuery("SELECT id, name, email FROM users WHERE id = 1");

if (rs.next()) {
    // Изменяем значение в ResultSet
    rs.updateString("name", "Иван Петров");
    rs.updateString("email", "ivan@example.com");
    
    // КРИТИЧНО: вызываем updateRow() чтобы сохранить в БД!
    rs.updateRow();
    
    System.out.println("Изменено в БД");
}

Пример 2: Вставка новой строки

Statement stmt = connection.createStatement(
    ResultSet.TYPE_SCROLL_SENSITIVE,
    ResultSet.CONCUR_UPDATABLE
);
ResultSet rs = stmt.executeQuery("SELECT id, name, email FROM users");

// Переходим на специальную строку для вставки
rs.moveToInsertRow();

// Заполняем значения
rs.updateInt("id", 100);
rs.updateString("name", "Новый пользователь");
rs.updateString("email", "new@example.com");

// Вставляем в БД
rs.insertRow();

// Возвращаемся к обычной позиции
rs.moveToCurrentRow();

Пример 3: Удаление строки

Statement stmt = connection.createStatement(
    ResultSet.TYPE_SCROLL_SENSITIVE,
    ResultSet.CONCUR_UPDATABLE
);
ResultSet rs = stmt.executeQuery("SELECT id, name FROM users WHERE id = 5");

if (rs.next()) {
    // Удаляем текущую строку из БД
    rs.deleteRow();
    System.out.println("Удалено из БД");
}

Практический пример: редактирование и сохранение

public void updateUserDetails(int userId) {
    try (Connection conn = getConnection()) {
        // Создаём updatable ResultSet
        Statement stmt = conn.createStatement(
            ResultSet.TYPE_SCROLL_SENSITIVE,
            ResultSet.CONCUR_UPDATABLE
        );
        
        ResultSet rs = stmt.executeQuery(
            "SELECT id, name, age, city FROM users WHERE id = " + userId
        );
        
        if (rs.next()) {
            // Меняем значения
            rs.updateString("name", "Александр");
            rs.updateInt("age", 35);
            rs.updateString("city", "Москва");
            
            // Сохраняем в БД
            rs.updateRow();
            System.out.println("Пользователь обновлён");
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
}

Ограничения Updatable ResultSet

⚠️ 1. Поддержка зависит от БД

DatabaseMetaData meta = connection.getMetaData();
boolean supportsUpdatable = meta.supportsResultSetConcurrency(
    ResultSet.TYPE_SCROLL_SENSITIVE,
    ResultSet.CONCUR_UPDATABLE
);

if (!supportsUpdatable) {
    System.out.println("Эта БД не поддерживает updatable ResultSet!");
}

Поддержка:

  • ✅ Oracle
  • ✅ SQL Server
  • ✅ Derby
  • ❌ PostgreSQL (не поддерживает нормально)
  • ❌ MySQL (не рекомендуется, работает плохо)

⚠️ 2. Требуется primary key в результатах

// ✅ Правильно — есть primary key
ResultSet rs = stmt.executeQuery(
    "SELECT id, name, email FROM users"
);

// ❌ Неправильно — нет primary key
ResultSet rs = stmt.executeQuery(
    "SELECT name, email FROM users"  // id отсутствует!
);

⚠️ 3. Проблемы с производительностью

// Очень медленно для больших результатов!
Statement stmt = connection.createStatement(
    ResultSet.TYPE_SCROLL_SENSITIVE,
    ResultSet.CONCUR_UPDATABLE
);
ResultSet rs = stmt.executeQuery("SELECT * FROM huge_table"); // 1 млн строк

// Это создаст огромный курсор в БД

⚠️ 4. Конфликты при параллельных изменениях

// Thread 1
rs.next();
rs.updateString("name", "Иван");
rs.updateRow();

// Thread 2 одновременно меняет ту же строку
// ResultSet может выкинуть исключение или перезаписать изменения

Почему это плохая идея

1. Не работает со всеми БД

// Выходит, что код не портабельный

2. Слабая типизация

// Ошибку в имени колонки узнаёте в runtime
rs.updateString("nmae", "Иван");  // Опечатка! Выкинет исключение

3. Сложно отлаживать

// Непонятно, когда именно данные пишутся в БД
// updateRow() может выкинуть исключение, и изменение откатится

Правильный подход (рекомендуемый)

Используйте параметризованные UPDATE запросы:

// ✅ Правильно и безопасно
String sql = "UPDATE users SET name = ?, email = ? WHERE id = ?";

try (PreparedStatement stmt = conn.prepareStatement(sql)) {
    stmt.setString(1, "Иван Петров");
    stmt.setString(2, "ivan@example.com");
    stmt.setInt(3, 1);
    
    int rowsUpdated = stmt.executeUpdate();
    if (rowsUpdated > 0) {
        System.out.println("Обновлено " + rowsUpdated + " строк");
    }
}

Или используйте ORM (JPA/Hibernate):

// ✅ Ещё лучше — ORM
@Transactional
public void updateUser(Long userId, String name, String email) {
    User user = userRepository.findById(userId).orElseThrow();
    user.setName(name);
    user.setEmail(email);
    userRepository.save(user);
}

Итоговая таблица

ПодходПлюсыМинусы
Updatable ResultSetОбъектно, можно менять как объектНе везде работает, медленно, сложно отлаживать
PreparedStatement UPDATEСтандартный, везде работает, быстроПроцедурный стиль
ORM (Hibernate)Объектно, безопасно, удобноЗависимость, оверхед

Практические выводы

Techniquely можно менять ResultSet и сохранять ❌ На практике не стоит — используй UPDATE запросы или ORM ⚠️ Updatable ResultSet — это антипаттерн, забытый реликт JDBC API

Для современного Java разработчика правильный ответ: "Можно, но это плохая идея. Используйте PreparedStatement с UPDATE или JPA.".

Можно ли поменять объект ResultSet и сразу же добавить его в базу данных? | PrepBro