1.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, которые умели себя сохранять в базу сами.

Но однажды все изменилось…

1.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.

1.3 Появление JPA

За де-факто пришло и признание де-юре. Разработчики JDK решили создать спецификацию по тому, как правильно мапить объекты на таблицы в базе данных. Эта спецификация называется JPA — Java Persistence API.

Это именно спецификация. Она описывает, как все должно работать и какими аннотациями нужно отмечать различные части класса, если мы хотим, чтобы его объекты сохранялись в базу данных.

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

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

  • JPA-стандарт
  • Hibernate Native API (дополнительная функциональность)

В официальной документации Hibernate это описывается так:

Но и на основе своего опыта, и после повторного прочтения документации по Hibernate я могу сказать, что JPA и Hibernate API совпадают на 95%. Это просто тождественные понятия.

1.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>