1. Мапінг класів на таблиці

Після вивчення JDBC у тебе швидше за все склалася думка, що працювати з базою даних із Java-програми — те ще задоволення. А якщо я скажу, що всю цю роботу можна зробити в 10 разів простіше?

У чому головна перевага мови SQL? Це декларативна мова — вона визначає, що ми хочемо отримати, і зовсім нічого не говорить про те, як це зробити. Як — це вже клопіт SQL-сервера.

Той самий підхід можна використовувати і при роботі з базами даних.

В ідеальному світі ми могли би просто писати SQL-запити до бази, а у відповідь нам приходили б готові Java-об'єкти, ну або колекції Java-об'єктів, якщо ми зробили запит на кілька штук.

Що сказати, саме так подумали кілька хлопців у 2000 році та вирішили написати свій ORM framework.

ORM розшифровується як Object-Relational Mapping і по суті є мапінгом Java-об'єктів на SQL-запити.

Хлопці вигадали дуже просту річ — кожній таблиці в базі даних повинен відповідати якийсь клас у Java-програмі. У програмі Java ми оперуємо об'єктами, а ці об'єкти вже вміють самі зберігати себе в базі даних.

Було три підходи до вирішення цього завдання, і вони виглядали приблизно так:

  1. Об'єкт сам себе зберігає в базі даних та оновлює свої поля на основі інформації з БД.
  2. Об'єкт вміє зберігати себе в базі даних, але ніколи не виступає ініціатором цієї справи.
  3. Об'єкт містить лише дані, і хтось його зберігає в базі даних і завантажує з БД.

Спочатку домінував перший підхід: тоді були популярні Application-сервери та Enterprise Java Beans. Був навіть цілий клас бінів, які мали назву Persistence EJB, які вміли себе зберігати в базу самі.

Але одного разу все змінилося...

2. Поява Hibernate

У 2001 році випустили першу версію фреймворку Hibernate. Це був простий фреймворк, але він дозволяв використовувати звичайні "дурні об'єкти", які нічого не знали про те, як їх потрібно зберігати до бази або завантажувати звідти.

Мапінг полів Java-класів і колонок у таблиці в базі встановлювався за допомогою XML-файлу. І іноді вони були досить громіздкими. Гаразд, кого я обманюю. Це були величезні полотна XML-коду. І ситуацію рятувало лише те, що 20 років тому не було таких гігантських баз даних, як зараз.

Але насправді найсильнішим рішенням було нарешті відокремити об'єкт, який потрібно зберігати в базу, від коду, який його туди зберігав. Це рішення насправді неочевидне. Тому що принцип інкапсуляції стверджує, що об'єкт найкраще знає про те, як його потрібно зберігати та завантажувати.

І підхід ORM справді ламає цю концепцію. Data-клас виставляє назовні своє внутрішнє влаштування, проте оперувати групами об'єктів різних типів стало значно простіше.

Серйозний прорив стався після виходу Java 5, коли в JDK з'явилися дві речі:

  • Анотації
  • Proxy

Анотації швидко витіснили XML, і тепер прямо в Java-класі можна було легко вказати всі потрібні налаштування для мапінгу Java-класу на таблицю в базі даних.

Проксі не такі помітні для користувача Hibernate, але їх внесок був ще серйознішим. Коли ти робиш запит на певний об'єкт або об'єкти у Hibernate, він просто повертає тобі заглушку (proxy) і перехоплює всі звернення до її методів.

Це дозволило реалізувати різні механізми Lazy Loading-у та підняло швидкість та ефективність роботи Hibernate на зовсім захмарний рівень для того часу. Hibernate став не просто стандартом галузі де-факто — його почали перекладати на інші мови. Так, наприклад, для C# з'явився Framework NHibernate.

3. Поява JPA

За де-факто прийшло й визнання де-юре. Розробники JDK вирішили створити специфікацію щодо того, як правильно мапити об'єкти на таблиці в базі даних. Ця специфікація називається JPA — Java Persistence API.

Це саме специфікація. Вона описує, як усе має працювати і якими анотаціями потрібно відзначати різні частини класу, якщо ми хочемо, щоб його об'єкти зберігалися до бази даних.

Таке відчуття, що хлопці просто взяли за основу Hibernate та поміняли у нього імена пакетів. Тому що всі анотації, які були в Hibernate, майже в тому ж вигляді переїхали до JPA.

Нині Hibernate повністю реалізує всю специфікацію JPA, а також має деякі додаткові можливості, які роблять роботу з ним ще комфортнішою. Тому з точки зору стандартизації можна сказати, що Hibernate має два набори функцій:

  • JPA-стандарт
  • Hibernate Native API (додаткова функціональність)

В офіційній документації Hibernate це описується так:

Але і на основі свого досвіду, і після повторного прочитання документації з Hibernate я можу сказати, що JPA та Hibernate API збігаються на 95%. Це просто тотожні поняття.

4. Maven для Hibernate

Якщо я вже так сильно розхвалив Hibernate, думаю, настав час перейти до роботи з ним трохи щільніше.

По-перше, є офіційний сайт, де просто купа англомовної документації. Вона, звісно, має ухил у довідкову інформацію, а не в навчальну. Але все одно краще так, ніж дебажити вихідники, чи не так? :)

Інструкція:

  1. Відкриваєш посилання.
  2. Довго на нього дивишся.
  3. Повертаєшся на JavaRush.
  4. Читаєш подальші лекції.

Моя робота полягає в тому, щоб спрощувати складні речі і пояснювати їх простими словами. І якщо твоє навчання дійшло до цього рівня, значить у мене це виходить.

Ну, а щоб приступити до роботи з Hibernate, тобі потрібно додати його до свого pom.xml. На сьогоднішній день доступна вже 6-та версія Hibernate, а точніше 6.1.1, тож будемо вчитися працювати з останньою версією.

Просто додай до свого pom.xml такі рядки:


<dependency>
<groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
<version>6.1.1.Final</version>
</dependency>

Якщо ти читаєш цю лекцію і за вікном 2023+ рік, тоді нову версію можна завантажити тут.

Важливо! Деякі бібліотеки, які використовує Hibernate, були виключені з JDK 11 та JDK 17, тому якщо у тебе виникають проблеми із запуском проєкту, додай до нього такі залежності:


  <dependency>
        <groupId>jakarta.xml.bind</groupId>
        <artifactId>jakarta.xml.bind-api</artifactId>
      <version>4.0.0</version>
  </dependency>
 
  <dependency>
        <groupId>org.glassfish.jaxb</groupId>
        <artifactId>jaxb-runtime</artifactId>
      <version>4.0.0</version>
  </dependency>
 
  <dependency>
        <groupId>org.javassist</groupId>
        <artifactId>javassist</artifactId>
        <version>3.29.0-GA</version>
  </dependency>