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

Что такое Result Set в JDBC?

1.3 Junior🔥 191 комментариев
#Soft Skills и карьера

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

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

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

Что такое ResultSet в JDBC?

Определение

ResultSet — это интерфейс в JDBC (Java Database Connectivity), который представляет результаты SQL запроса в виде таблицы с строками и столбцами. Это главный способ получения данных из базы данных в Java приложении.

Иерархия

Statement
    ↓
executeQuery() → ResultSet

Когда вы выполняете SELECT запрос через Statement, вы получаете ResultSet.

Как работает ResultSet

┌─────────────────────────────────┐
│ ResultSet (таблица)             │
├─────────────────────────────────┤
│ Column1  │ Column2  │ Column3   │
├─────────────────────────────────┤
│ value1a  │ value2a  │ value3a   │  ← Курсор начинает здесь
│ value1b  │ value2b  │ value3b   │
│ value1c  │ value2c  │ value3c   │
└─────────────────────────────────┘

Курсор (pointer) указывает на текущую строку. Вначале он находится до первой строки.

Основные методы ResultSet

1. Навигация по строкам

import java.sql.*;

public class ResultSetNavigation {
    
    public void demonstrateNavigation(ResultSet rs) throws SQLException {
        
        // next() - переместиться на следующую строку
        while (rs.next()) {
            System.out.println("Current row: " + rs.getRow());
        }
        
        // previous() - переместиться на предыдущую строку
        // (только если ResultSet позволяет)
        if (rs.isBeforeFirst()) {
            System.out.println("Курсор перед первой строкой");
        }
        
        // absolute(n) - переместиться на n-ую строку
        rs.absolute(5);  // Перейти на строку 5
        
        // relative(n) - переместиться на n строк относительно текущей
        rs.relative(3);  // Переместиться на 3 строки вперед
        
        // first() / last() - в начало / конец
        rs.first();      // Первая строка
        rs.last();       // Последняя строка
        
        // getRow() - текущий номер строки
        int currentRow = rs.getRow();
    }
}

2. Получение данных из текущей строки

public class DataRetrieval {
    
    public void retrieveData(ResultSet rs) throws SQLException {
        
        while (rs.next()) {
            // Получение по индексу столбца (начиная с 1, не 0!)
            String name = rs.getString(1);
            int age = rs.getInt(2);
            double salary = rs.getDouble(3);
            
            // ИЛИ получение по имени столбца (рекомендуется)
            String firstName = rs.getString("first_name");
            String lastName = rs.getString("last_name");
            java.util.Date birthDate = rs.getDate("birth_date");
            
            // Различные типы данных
            boolean isActive = rs.getBoolean("is_active");
            byte[] data = rs.getBytes("photo");
            java.sql.Timestamp timestamp = rs.getTimestamp("created_at");
            
            // Проверка NULL
            if (rs.wasNull()) {
                System.out.println("Значение было NULL");
            }
        }
    }
}

Типы ResultSet

ResultSet имеет два важных параметра при создании:

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

Statement stmt = connection.createStatement(
    ResultSet.TYPE_FORWARD_ONLY,
    ResultSet.CONCUR_READ_ONLY
);

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

// Можно только двигаться вперед
while (rs.next()) {
    // Обработка строки
}
// rs.previous() - НЕ сработает!

2. TYPE_SCROLL_INSENSITIVE

Statement stmt = connection.createStatement(
    ResultSet.TYPE_SCROLL_INSENSITIVE,
    ResultSet.CONCUR_READ_ONLY
);

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

// Можно перемещаться в любую сторону
rs.last();              // На конец
rs.previous();          // На предыдущую
rs.first();             // На начало
rs.absolute(10);        // На 10-ую строку

3. TYPE_SCROLL_SENSITIVE

Statement stmt = connection.createStatement(
    ResultSet.TYPE_SCROLL_SENSITIVE,
    ResultSet.CONCUR_UPDATABLE
);

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

// Видны изменения, сделанные другими соединениями
// Можно обновлять данные напрямую
rs.next();
rs.updateString("name", "New Name");
rs.updateRow();  // Обновит БД

Полный пример работы с ResultSet

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class UserRepository {
    
    private Connection connection;
    
    public UserRepository(Connection connection) {
        this.connection = connection;
    }
    
    public User findUserById(long id) throws SQLException {
        String sql = "SELECT id, name, email, age FROM users WHERE id = ?";
        
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setLong(1, id);
            
            try (ResultSet rs = stmt.executeQuery()) {
                // next() возвращает true если есть строка
                if (rs.next()) {
                    return mapRowToUser(rs);
                }
            }
        }
        
        return null;  // Пользователь не найден
    }
    
    public List<User> findAllUsers() throws SQLException {
        String sql = "SELECT id, name, email, age FROM users";
        List<User> users = new ArrayList<>();
        
        try (Statement stmt = connection.createStatement();
             ResultSet rs = stmt.executeQuery(sql)) {
            
            // Итерируемся по всем строкам
            while (rs.next()) {
                User user = mapRowToUser(rs);
                users.add(user);
            }
        }
        
        return users;
    }
    
    public List<User> findUsersByAgeRange(int minAge, int maxAge) throws SQLException {
        String sql = "SELECT id, name, email, age FROM users WHERE age BETWEEN ? AND ?";
        List<User> users = new ArrayList<>();
        
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setInt(1, minAge);
            stmt.setInt(2, maxAge);
            
            try (ResultSet rs = stmt.executeQuery()) {
                while (rs.next()) {
                    users.add(mapRowToUser(rs));
                }
            }
        }
        
        return users;
    }
    
    private User mapRowToUser(ResultSet rs) throws SQLException {
        // Рекомендуется использовать имена столбцов, а не индексы
        return new User(
            rs.getLong("id"),
            rs.getString("name"),
            rs.getString("email"),
            rs.getInt("age")
        );
    }
}

class User {
    private long id;
    private String name;
    private String email;
    private int age;
    
    public User(long id, String name, String email, int age) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.age = age;
    }
    
    // getters/setters...
}

Обработка различных типов данных

public void processDifferentTypes(ResultSet rs) throws SQLException {
    while (rs.next()) {
        // Строки
        String name = rs.getString("name");
        
        // Числа
        int count = rs.getInt("count");
        long id = rs.getLong("id");
        double price = rs.getDouble("price");
        java.math.BigDecimal exactPrice = rs.getBigDecimal("exact_price");
        
        // Даты и время
        java.sql.Date date = rs.getDate("date_field");
        java.sql.Time time = rs.getTime("time_field");
        java.sql.Timestamp timestamp = rs.getTimestamp("timestamp_field");
        
        // Бинарные данные
        byte[] binaryData = rs.getBytes("binary_field");
        java.io.InputStream stream = rs.getBinaryStream("blob_field");
        
        // Boolean
        boolean isActive = rs.getBoolean("is_active");
        
        // Object для специальных типов
        Object genericObject = rs.getObject("some_field");
    }
}

Метаданные ResultSet

public void getMetadata(ResultSet rs) throws SQLException {
    ResultSetMetaData metadata = rs.getMetaData();
    
    // Количество столбцов
    int columnCount = metadata.getColumnCount();
    System.out.println("Столбцов: " + columnCount);
    
    // Информация о каждом столбце
    for (int i = 1; i <= columnCount; i++) {
        String columnName = metadata.getColumnName(i);
        String columnType = metadata.getColumnTypeName(i);
        int precision = metadata.getPrecision(i);
        
        System.out.println(columnName + " (" + columnType + ")");
    }
}

Проблемы и best practices

1. Утечка ресурсов

// ❌ Плохо - ресурсы не закрываются
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");

// ✅ Хорошо - используем try-with-resources
try (Statement stmt = connection.createStatement();
     ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {
    while (rs.next()) {
        // обработка
    }
}  // Автоматически закроется

2. Правильная индексация

// ❌ Неправильно
String name = rs.getString(0);  // Индексы начинаются с 1!

// ✅ Правильно
String name = rs.getString(1);  // или rs.getString("name")

3. Обработка NULL

// ❌ Может выбросить NullPointerException
String value = rs.getString("nullable_field");
int length = value.length();

// ✅ Правильная обработка
String value = rs.getString("nullable_field");
if (rs.wasNull()) {
    System.out.println("Значение NULL");
}

Итоговый вывод

ResultSet — это фундаментальный интерфейс JDBC для работы с результатами SQL запросов. Он предоставляет удобный способ навигации по строкам и извлечения данных со столбцов. Правильное использование ResultSet с try-with-resources и обработкой NULL является важной частью надежного работы с базами данных в Java.