Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое 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.