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

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

1.0 Junior🔥 111 комментариев
#Базы данных и SQL

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

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

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

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

Краткий ответ: Да, можно, но это требует специальной конфигурации ResultSet и не является типичной практикой. Рассмотрю основные подходы.

Что такое updatable ResultSet

Результаты запросов по умолчанию доступны только для чтения (read-only). Для изменения данных через ResultSet нужен updatable ResultSet:

// ✅ Создаём updatable ResultSet
Statement stmt = connection.createStatement(
    ResultSet.TYPE_SCROLL_SENSITIVE,        // Можем перемещаться
    ResultSet.CONCUR_UPDATABLE              // Можем изменять
);
ResultSet rs = stmt.executeQuery("SELECT id, name, salary FROM employees WHERE id = 1");

if (rs.next()) {
    // Изменяем значение в ResultSet
    rs.updateString("name", "John Doe");    // Меняем имя
    rs.updateDouble("salary", 75000.00);    // Меняем зарплату
    
    // Немедленно сохраняем в БД
    rs.updateRow();  // ✅ UPDATE запрос отправится в БД
}

rs.close();
stmt.close();

Требования для updatable ResultSet

Далеко не все запросы поддерживают обновление:

// ✅ Поддерживает обновление
Statement stmt = connection.createStatement(
    ResultSet.TYPE_SCROLL_SENSITIVE,
    ResultSet.CONCUR_UPDATABLE
);
ResultSet rs = stmt.executeQuery("SELECT id, name FROM users");
// Работает: простой SELECT одной таблицы

// ❌ НЕ поддерживает обновление
ResultSet rs = stmt.executeQuery(
    "SELECT u.id, u.name, COUNT(o.id) as order_count " +
    "FROM users u JOIN orders o ON u.id = o.user_id " +
    "GROUP BY u.id"  // JOIN + GROUP BY
);
// Ошибка: немодифицируемый ResultSet

// ❌ Также не поддерживает
ResultSet rs = stmt.executeQuery(
    "SELECT * FROM (SELECT * FROM users) AS subquery"
    // Подзапросы
);

Пример: Практическое использование

public void updateEmployeeSalary(Connection conn, int employeeId, double newSalary) 
        throws SQLException {
    
    // Используем PreparedStatement с updatable ResultSet
    PreparedStatement pstmt = conn.prepareStatement(
        "SELECT id, name, salary FROM employees WHERE id = ?",
        ResultSet.TYPE_SCROLL_SENSITIVE,
        ResultSet.CONCUR_UPDATABLE
    );
    
    pstmt.setInt(1, employeeId);
    ResultSet rs = pstmt.executeQuery();
    
    if (rs.next()) {
        double currentSalary = rs.getDouble("salary");
        System.out.println("Current salary: " + currentSalary);
        
        // Изменяем
        rs.updateDouble("salary", newSalary);
        
        // Сохраняем (выполняет UPDATE в БД)
        rs.updateRow();
        
        System.out.println("Salary updated to: " + newSalary);
    }
    
    rs.close();
    pstmt.close();
}

Методы для работы с updatable ResultSet

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

// Методы обновления
rs.updateString(columnName, value);      // Обновить строку
rs.updateInt(columnName, value);         // Обновить число
rs.updateDouble(columnName, value);      // Обновить double
rs.updateBoolean(columnName, value);     // Обновить булево
rs.updateDate(columnName, value);        // Обновить дату
rs.updateNull(columnName);               // Установить NULL

rs.updateRow();                          // ✅ Сохранить изменения (UPDATE)
rs.cancelRowUpdates();                   // Отменить изменения
rs.deleteRow();                          // Удалить строку (DELETE)
rs.insertRow();                          // Добавить новую строку (INSERT)

Полный пример с множественными операциями

public void manageBatchUpdates(Connection conn) throws SQLException {
    
    PreparedStatement pstmt = conn.prepareStatement(
        "SELECT id, name, salary, department FROM employees WHERE department = ?",
        ResultSet.TYPE_SCROLL_SENSITIVE,
        ResultSet.CONCUR_UPDATABLE
    );
    
    pstmt.setString(1, "Engineering");
    ResultSet rs = pstmt.executeQuery();
    
    while (rs.next()) {
        int id = rs.getInt("id");
        String name = rs.getString("name");
        double salary = rs.getDouble("salary");
        
        // Условная логика
        if (salary < 50000) {
            rs.updateDouble("salary", salary * 1.1);  // +10%
            rs.updateRow();  // Сохраняем
            System.out.println("Raised salary for " + name);
        }
    }
    
    // Добавляем нового сотрудника
    rs.moveToInsertRow();  // Переходим на специальную строку для вставки
    rs.updateInt("id", 1001);
    rs.updateString("name", "Jane Smith");
    rs.updateDouble("salary", 60000);
    rs.updateString("department", "Engineering");
    rs.insertRow();  // ✅ INSERT в БД
    rs.moveToCurrentRow();  // Возвращаемся к основному курсору
    
    rs.close();
    pstmt.close();
}

Почему это редко используется на практике

Хотя updatable ResultSet технически работает, есть множество причин избегать его:

1. Производительность

// ❌ Медленно: построчное обновление
while (rs.next()) {
    rs.updateDouble("salary", rs.getDouble("salary") * 1.1);
    rs.updateRow();  // Каждое обновление — отдельный запрос
}

// ✅ Быстро: batch update
String sql = "UPDATE employees SET salary = salary * 1.1 WHERE department = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "Engineering");
int affectedRows = pstmt.executeUpdate();  // Один запрос для всех

2. Ограничения драйвера

Не все БД-драйверы полностью поддерживают updatable ResultSet:

// ❌ MySQL InnoDB может иметь проблемы с некоторыми типами запросов
// ❌ Oracle требует специальных привилегий
// ❌ PostgreSQL имеет ограниченную поддержку

3. Проблемы с конкурентностью

// ❌ Race condition: между чтением и обновлением
rs.next();
rs.getDouble("salary");  // Прочитали значение
// Другой процесс мог его изменить!
rs.updateDouble("salary", newValue);
rs.updateRow();  // Перезаписали изменения другого процесса

4. Читаемость кода

// ❌ Не очевидно, что происходит UPDATE
rs.updateDouble("salary", value);
rs.updateRow();

// ✅ Явный UPDATE SQL более понятен
pstmt = conn.prepareStatement("UPDATE employees SET salary = ? WHERE id = ?");
pstmt.setDouble(1, value);
pstmt.setInt(2, employeeId);
pstmt.executeUpdate();

Правильный подход

Используй updatable ResultSet ТОЛЬКО если:

  1. Нужны данные для чтения И изменения одновременно
  2. Количество изменений небольшое (< 1000)
  3. Запрос простой (одна таблица без JOIN)
  4. Производительность не критична

В остальных случаях:

// ✅ Рекомендуемый подход: SELECT → обработка в коде → INSERT/UPDATE

// 1. Читаем данные
Statement stmt = conn.createStatement();  // Простой read-only ResultSet
ResultSet rs = stmt.executeQuery("SELECT id, name, salary FROM employees");

List<Employee> employees = new ArrayList<>();
while (rs.next()) {
    Employee emp = new Employee();
    emp.setId(rs.getInt("id"));
    emp.setName(rs.getString("name"));
    emp.setSalary(rs.getDouble("salary"));
    employees.add(emp);
}

// 2. Обрабатываем в коде (более гибко)
for (Employee emp : employees) {
    if (emp.getSalary() < 50000) {
        emp.setSalary(emp.getSalary() * 1.1);
    }
}

// 3. Сохраняем обновления
PreparedStatement updateStmt = conn.prepareStatement(
    "UPDATE employees SET salary = ? WHERE id = ?"
);

for (Employee emp : employees) {
    updateStmt.setDouble(1, emp.getSalary());
    updateStmt.setInt(2, emp.getId());
    updateStmt.addBatch();
}

updateStmt.executeBatch();  // ✅ Все обновления сразу

Заключение

Да, можно менять значения в ResultSet и сохранять их через updateRow(), но это требует:

  • ResultSet.TYPE_SCROLL_SENSITIVE
  • ResultSet.CONCUR_UPDATABLE
  • Простых SELECT запросов (одна таблица)

Однако в современной разработке лучше:

  1. Читать данные обычным read-only ResultSet
  2. Обрабатывать их в коде приложения
  3. Сохранять через явный UPDATE SQL или ORM (Hibernate, JPA)

Это обеспечивает лучшую производительность, читаемость и контроль над БД операциями.