Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Курсор в базе данных
Курсор (Cursor) — это объект в базе данных, который действует как временный указатель на результаты SQL запроса. Курсор позволяет обрабатывать результаты построчно, вместо того чтобы загружать все результаты сразу в память.
Как работает курсор
Курсор функционирует как указатель, который:
- Выполняет запрос и сохраняет результаты в буфере
- Указывает на текущую строку в наборе результатов
- Перемещается по строкам (fetch next row)
- Закрывается после обработки всех данных
public class CursorExample {
public static void main(String[] args) {
String url = "jdbc:postgresql://localhost:5432/mydb";
String query = "SELECT id, name, salary FROM employees WHERE dept_id = 10";
try (Connection conn = DriverManager.getConnection(url, "user", "pass");
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query)) {
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
double salary = rs.getDouble("salary");
System.out.printf("ID: %d, Name: %s, Salary: %.2f%n", id, name, salary);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Типы курсоров
1. Forward-Only Cursor (Однонаправленный)
Может двигаться только вперед (FETCH NEXT):
Statement stmt = conn.createStatement(
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY
);
Преимущества: быстрый, мало памяти
2. Static Cursor (Статический)
Зафиксирует снимок данных на момент открытия курсора:
Statement stmt = conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY
);
3. Scroll Cursor (Прокручиваемый)
Позволяет двигаться в любом направлении:
Statement stmt = conn.createStatement(
ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_UPDATABLE
);
ResultSet rs = stmt.executeQuery(query);
rs.next();
rs.previous();
rs.absolute(10);
rs.first();
rs.last();
Движение по ResultSet
ResultSet rs = stmt.executeQuery(query);
rs.next();
rs.previous();
rs.relative(5);
rs.absolute(10);
rs.first();
rs.last();
if (rs.isBeforeFirst()) System.out.println("Перед первой");
if (rs.isAfterLast()) System.out.println("После последней");
if (rs.isFirst()) System.out.println("На первой");
Обновляемые курсоры
public class UpdatableCursor {
public static void main(String[] args) {
String url = "jdbc:postgresql://localhost:5432/mydb";
String query = "SELECT id, name, salary FROM employees FOR UPDATE";
try (Connection conn = DriverManager.getConnection(url, "user", "pass");
Statement stmt = conn.createStatement(
ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = stmt.executeQuery(query)) {
while (rs.next()) {
String name = rs.getString("name");
double salary = rs.getDouble("salary");
double newSalary = salary * 1.1;
rs.updateDouble("salary", newSalary);
rs.updateRow();
System.out.printf("%s: %.2f to %.2f%n", name, salary, newSalary);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Курсоры в хранимых процедурах
public class StoredProcedureCursor {
public static void main(String[] args) {
try (Connection conn = getConnection();
CallableStatement call = conn.prepareCall("{call process_employees()}")) {
call.execute();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
SQL пример (PostgreSQL):
CREATE PROCEDURE process_employees()
LANGUAGE plpgsql
AS $$
DECLARE
emp_cursor CURSOR FOR
SELECT id, name, salary FROM employees;
emp_id INT;
emp_name VARCHAR;
BEGIN
OPEN emp_cursor;
LOOP
FETCH emp_cursor INTO emp_id, emp_name;
EXIT WHEN NOT FOUND;
RAISE NOTICE 'Employee: %', emp_name;
END LOOP;
CLOSE emp_cursor;
END;
$$;
Когда использовать курсоры
Используй курсоры когда:
- Нужна построчная обработка (не пакетная)
- Работаешь с очень большими наборами данных
- Логика сложная и зависит от каждой строки
- Нужна возможность обновления во время обработки
ResultSet rs = stmt.executeQuery(
"SELECT id, text, score FROM reviews WHERE processed = false");
while (rs.next()) {
String text = rs.getString("text");
int score = rs.getInt("score");
String sentiment = analyzeSentiment(text);
int updatedScore = calculateScore(score, sentiment);
rs.updateInt("score", updatedScore);
rs.updateBoolean("processed", true);
rs.updateRow();
}
НЕ используй курсоры когда:
- Можно использовать SQL операции (JOIN, GROUP BY, UPDATE)
- Нужны просто все данные — загрузи их сразу
- Нужна высокая производительность
Производительность
// Медленно (построчное обновление)
while (rs.next()) {
int id = rs.getInt("id");
PreparedStatement update = conn.prepareStatement("UPDATE ... WHERE id = ?");
update.setInt(1, id);
update.executeUpdate();
}
// Быстро (пакетное обновление)
String updateSql = "UPDATE employees SET salary = salary * 1.1 WHERE dept = ?";
PreparedStatement update = conn.prepareStatement(updateSql);
update.setString(1, "IT");
update.executeUpdate();
Закрытие курсора
try (ResultSet rs = stmt.executeQuery(query)) {
while (rs.next()) {
// обработка
}
} // Автоматически закрывается при выходе из try-with-resources
// Или вручную
rs.close();
stmt.close();
Выводы
- Курсор — указатель на результаты запроса
- Forward-only — только вперед, быстро
- Scroll — в любом направлении
- Updatable — позволяет обновлять данные
- В Java: ResultSet — это курсор
- Best practice: используй SQL для пакетных операций, курсоры для сложной логики
- Всегда закрывай курсоры (try-with-resources автоматизирует это)