1. Типи даних

Давай звернемо увагу на таблицю:

"C:\Program Files\Java\jdk-17.0.3.1\bin\java.exe…
idjava.lang.IntegerINT4
namejava.lang.StringVARCHAR12
leveljava.lang.IntegerINT4
created_datejava.sql.DateDATE91
Process finished with exit code 0

У третій колонці ми бачимо типи: INT, VARCHAR, DATE. Це типи SQL-сервера. Отже, сервер віддає дані з тими типами, про які він знає. Як же ці типи перетворюються на типи Java?

Це і є одна з речей, яку було стандартизовано разом з JDBC. Автори JDBC почали з того, що зафіксували список SQL-типів. Є спеціальний клас із константами:


class java.sql.Types {
   public static final int CHAR = 1;
   public static final int NUMERIC = 2;
   public static final int DECIMAL = 3;
   public static final int INTEGER = 4;
   public static final int FLOAT = 6;
   public static final int REAL = 7;
  …
}

Число — це не порядковий номер у класі, а ID-тип у списку SQL-типів у специфікації SQL. Саме ці числа ти бачив у прикладі на початку лекції.

Також у класі ResultSet є методи, які вміють перетворювати одні типи даних на інші. Не всі типи можна перетворити один на одного, але логіка досить зрозуміла. Ось тобі хороша таблиця спочатку:

Метод Тип даних SQL
int getInt() NUMERIC, INTEGER, DECIMAL
float getFloat() NUMERIC, INTEGER, DECIMAL, FLOAT, REAL
double getDoublel() NUMERIC, INTEGER, DECIMAL, FLOAT, REAL
Date getDate() DATE, TIME, TIMESTAMP
Time getTime() DATE, TIME, TIMESTAMP
Timestamp getTimestamp() DATE, TIME, TIMESTAMP
String getString() CHAR, VARCHAR

Серед усіх SQL-типів можна явно виділити деякі групи:

  • числа
  • час
  • рядки
  • байтові об'єкти

До речі, а ти звернув увагу на метод getInt()?

2. JDBC та NULL

Ти звернув увагу, що не так з методом getInt() у класу ResultSet? Давай подивимося ще раз на його сигнатуру:


int getInt(column)

Цей метод повертає int, а не Integer, тому що під час створення JDBC-типу Integer ще не було. Гаразд, припустимо. Тоді маю запитання:

У мене є таблиця в базі даних, що має колонку з типом INT NULL, яка може набувати значень INT, але також може бути й NULL. Як мені отримати значення null із цієї колонки?

Не хвилюйся, все продумали вже до тебе.

Рішення перше. Якщо SQL-тип у Java представлений типом посилань, як-от Date або String, то проблеми немає. Змінні цього типу можуть набувати null.

Рішення друге. Примітивні типи не можуть приймати значення null, тому методи типу getInt() просто повертатимуть значення за замовчуванням. Для int це 0, для float = 0.0f, для double = 0.0d тощо.

А як тоді зрозуміти, що було в колонці: 0 чи NULL? І на це питання партія має відповідь.

Рішення третє. У класу ResultSet є спеціальний метод, який повертає true, якщо щойно замість NULL метод повернув інше значення.

Все працює саме так, як я написав. Приклад:


ResultSet results = statement.executeQuery("SELECT * FROM user");
results.next();
int level = results.getInt("level");
 
if (results.wasNull()) {
  System.out.println("Level is null");
} else {
  System.out.println("Level is " + level);
}

Якщо під час звернення до методу getInt() він повинен був повернути null, то метод wasNull() поверне true, інакше метод wasNull() поверне false.

Це працює не тільки для примітивних типів:


ResultSet results = statement.executeQuery("SELECT * FROM user");
results.next();
String name = results.getString("name");
 
if (results.wasNull()) {
  System.out.println("Name is null");
} else {
  System.out.println("User name is " + name);
}

Це, звісно, костиль. Натомість жодних проблем з NullPointerException. Потрібно у всьому бачити позитивні сторони :)

3. Що не так з типами даних у JDBC?

Продовжуємо тест на уважність. Подивися на метод getDate(column). Що з ним не так? Цей метод має такий тип результату:


java.sql.Date

Він може зберігати null, і це вже непогано. Але таки з ним щось не так. Підказка! Ось як виглядає правильний тип Date:


java.util.Date

У них різні пакети! Це взагалі різні типи даних. А причина ось у чому...

Бази даних ще з 70-х років 20 століття підтримують 3 типи даних для зберігання часу:

  • DATE — зберігає дату: рік, місяць, день.
  • TIME — зберігає час: години, хвилини, секунди.
  • TIMESTAMP — зберігає конкретний момент часу: дата, час та мілісекунди.

Мова Java перші 10 років свого існування мала лише один тип даних — java.util.Date, який зберігав момент часу у форматі UNIX TIME: кількість мілісекунд, яка пройшла з початку 1970 року.

Тому творці стандарту JDBC додали до Java ще три типи даних — спеціально для JDBC:

  • java.sql.Date
  • java.sql.Time
  • java.sqlTimestamp

І тому методи інтерфейсу ResultSet містять фіксовані типи даних:

SQL TYPE Java Type Метод
DATE java.sql.Date java.sql.Date getDate()
TIME java.sql.Time java.sql.Time getTime()
TIMESTAMP java.sql.Timestamp java.sql.Timestamp getTimestamp()

І саме цей тип ти бачиш тут:

"C:\Program Files\Java\jdk-17.0.3.1\bin\java.exe…
idjava.lang.IntegerINT4
namejava.lang.StringVARCHAR12
leveljava.lang.IntegerINT4
created_datejava.sql.DateDATE91
Process finished with exit code 0

Здогадуєшся, чого тут не вистачає? Типів даних, які з'явилися в Java DateTime API:

  • LocalDate
  • LocalTime
  • LocalDateTime