1. Управління поточним рядком
Першу програму ми написали, і вона чудово відпрацювала. Ми написали запит, виконали його, і в результаті метод executeQuery() повернув нам об'єкт ResultSet, який містить усі результати запиту. Тепер ми спробуємо розібратися, як ці результати отримати.
Результат запиту може містити тисячі рядків і сотні колонок різних типів, тому це не таке тривіальне завдання, як тобі здається. Наприклад, в базі можуть зберігатися картинки: тоді ти можеш отримати картинку у вигляді набору байт або InputStream для її завантаження.
Але почнемо ми з найпростішого — з поняття “поточного рядка результату”. Оскільки рядків результату зазвичай дуже багато, об'єкт ResultSet містить покажчик на поточний рядок, і послідовно перемикає рядки для їх читання за допомогою методу next().
Такий підхід насамперед використано для оптимізації. JDBC Driver може не завантажувати рядки з бази, поки ти послідовно не дійдеш до читання. FileInputStream ти теж читаєш послідовно — з початку до кінця. Отже такий підхід має бути тобі знайомим і зрозумілим.
Але ж, якщо тобі дуже треба, то файли можна читати в будь-якому місці за допомогою класу RandomAccessFile.
Клас ResultSet теж дозволяє щось подібне і дає змогу рухати поточний рядок за результатом куди завгодно. Для цього він має такі методи:
Метод | Опис | |
---|---|---|
1 | next() | Переключитися на наступний рядок |
2 | previous() | Переключитися на попередній рядок |
3 | isFirst() | Поточний рядок перший? |
4 | isBeforeFirst() | Ми перед першим рядком? |
5 | isLast() | Поточний рядок останній? |
6 | isAfterLast() | Ми після останнього рядка? |
7 | absolute(int n) | Робить N-й рядок поточним |
8 | relative(int n) | Пересуває поточний рядок на N позицій уперед. N може бути <0 |
9 | getRow() | Повертає номер рядка |
Методи досить прості, проте важливо додати два пояснення. Результати ніби обрамлені порожніми рядками з обох боків. Тому спочатку поточний рядок знаходиться перед першим рядком результату. І щоб отримати перший рядок, потрібно хоча б один раз викликати метод next().
Якщо ти на останньому рядку викликав метод next(), ти перейшов на рядок після останнього. Даних з нього прочитати ти не можеш, але жодної помилки не станеться. Тут метод isAfterLast() буде повертати true як результат.
Приклад:
Statement statement = connection.createStatement();
ResultSet results = statement.executeQuery("SELECT * FROM user");
System.out.println( results.getRow() ); // 0
System.out.println( results.isBeforeFirst() ); // true
System.out.println( results.isFirst() ); // false
results.next();
System.out.println( results.getRow() ); // 1
System.out.println( results.isBeforeFirst() ); // false
System.out.println( results.isFirst() ); // true
results.next();
System.out.println( results.getRow() ); // 2
System.out.println( results.isBeforeFirst() ); // false
System.out.println( results.isFirst() ); // false
2. Отримання даних із поточного рядка
Ти навчився віртуозно керувати поточним рядком. Тепер давай розберемо, як із нього отримувати дані. Для цього в об'єкта ResultSet є спеціальні методи, які можна описати одним шаблоном:
getType(номерКолонки)
Втім, якщо колонка має ім'я, можна отримувати і за ім'ям колонки:
getType(ім'яКолонки)
Приклад:
while (results.next()) {
Integer id = results.getInt(“id”);
String name = results.getString("name");
System.out.println(results.getRow() + "." + id + "\t"+ name);
}
Нижче я наведу таблицю, яка допоможе тобі зв'язати типи даних SQL та методи ResultSet:
SQL Datatype | getXXX() Methods |
---|---|
CHAR | getString() |
VARCHAR | getString() |
INT | getInt() |
FLOAT | getDouble() |
CLOB | getClob() |
BLOB | getBlob() |
DATE | getDate() |
TIME | getTime() |
TIMESTAMP | getTimestamp() |
Суть, думаю, зрозуміла.
3. Отримання різних даних про ResultSet
Як читати дані з поточного рядка ми розібралися: і за номером колонки, і за іменем. До речі, а як дізнатись ім'я колонки за її номером? Чи кількість колонок у результаті?
З одного боку, якщо ти пишеш запит, то ти начебто мусиш усе це знати. З іншого боку, ми можемо писати програму, яка відображає результат запиту на екран: запит нам надсилають, і ми хочемо просто відобразити на екрані (в консолі, на вебсторінці) все, що нам повернув SQL-сервер.
Для цього JDBC має спеціальний об'єкт — інтерфейс ResultSetMetaData. Отримати цей об'єкт доволі просто:
Statement statement = connection.createStatement();
ResultSet results = statement.executeQuery("SELECT * FROM user");
ResultSetMetaData resultSetMetaData = results.getMetaData();
Інтерфейс ResultSetMetaData має дуже цікаві методи. Нижче наведу найпопулярніші з них:
1 | getColumnCount() | Повертає кількість колонок результату |
2 | getColumnName(int column) | Повертає ім'я колонки |
3 | getColumnLabel(int column) | Повертає description колонки |
4 | getColumnType() | Повертає тип колонки: число (спеціальний код) |
5 | getColumnTypeName() | Повертає тип колонки: рядок |
6 | getColumnClassName() | Повертає ім'я Java-класу типу колонки |
7 | getTableName() | Повертає ім'я таблиці |
8 | getCatalogName() | Повертає ім'я каталогу колонки |
9 | getSchemaName() | Повертає ім'я схеми бази даних |
10 | isAutoIncrement(int column) | Колонка підтримує AUTO INCREMENT? |
11 | isNullable() | Колонка може містити NULL? |
Давай з його допомогою дізнаємося трохи більше про нашу таблицю:
ResultSetMetaData metaData = results.getMetaData();
int columnCount = metaData.getColumnCount();
for (int column = 1; column <= columnCount; column++)
{
String name = metaData.getColumnName(column);
String className = metaData.getColumnClassName(column);
String typeName = metaData.getColumnTypeName(column);
int type = metaData.getColumnType(column);
System.out.println(name + "\t" + className + "\t" + typeName + "\t" + type);
}
Важливо! Зверни увагу, що колонки нумеруються з 1. Рядки, до речі, теж. Як це незвично, чи не так?
І ось який результат отримуємо після запуску програми:
"C:\Program Files\Java\jdk-17.0.3.1\bin\java.exe… | |||
id | java.lang.Integer | INT | 4 |
name | java.lang.String | VARCHAR | 12 |
level | java.lang.Integer | INT | 4 |
created_date | java.sql.Date | DATE | 91 |
Process finished with exit code 0 |
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ