Hello, Hibernate (без JPA)

Hibernate_logo_aJDBC предоставляет вполне достаточный интерфейс для работы с базами данных в Java. Однако этот интерфейс весьма многословен и довольно неудобен и даже Spring JDBC не делает его сильно лучше. По сути дела проблема в том, что реляционные базы данных работают с таблицами и отношениями между ними, в то время как в Java работают с объектами и их иерархиями. Поэтому приходится для каждого объекта или таблицы писать класс отображения одного в другое. Этот процесс называется ORM — object-relational mapping (объектно-реляционное отображение). И, к счастью, существуют готовые ORM решения, которые сами переводят данные из одного вида в другой и обратно.

— один из старейших и уж точное наиболее распространённый ORM фреймворк в мире Java. Он может быть использован в качестве одной из JPA реализаций, либо с использованием его собственного API, которое, с одной стороны, сильно напоминает JPA, с другой стороны предоставляет больше возможностей и гибкости, чем строго регламентированный JPA.

Подготовка

В пустой maven проект добавим встраиваемую базу и артефакты Hibernate:

hibernate-core это, собственно, сам Hibernate, API и его реализация, а hibernate-java8 добавляет поддержку новых типов данных (в основном даты/времени), появивишихся в java8.

Entity

Все классы, которые могут быть сохранены в базе данных называются entity(сущность) и на них налагаются определённые требования:

  • Наличие публично доступного конструктора без аргументов
  • Класс, его методы и сохраняемые поля не должны быть final
  •  Если объект Entity класса будет передаваться по значению как отдельный объект (detached object), например через удаленный интерфейс (through a remote interface), он так же должен реализовывать Serializable интерфейс.
  • Сохраняемые поля должны быть доступны только с использованием методов класса.

Эти требования в точности соответствуют требованиям JPA.

Каждый сохраняемый класс помечается аннотацией @Entity, говорящей Hibernate, что этот класс является сущностью. Помимо того, в каждом классе, помеченном @Entity должно быть поле, имеющее аннотацию @Id, говорящее Hibernate, что это поле может быть использовано как первичный ключ в базе данных и что по значению этого поля Hibernate может отличать один объект от другого. Честно говоря, полей с @Id может быть несколько и механизм первичного ключа несколько сложнее, но я рассмотрю это в отдельной статье. Стоит отметить, что Hibernate использует аннотации из JPA для определения сущностей. Раньше поддерживались и собственные аннотации Hibernate, но с недавних пор они признаны устаревшими.

В примере у нас будет простой entity класс, с тремя полями:

Настройка Hibernate

После создания классов данных и описания их отображения в базу данных, необходимо сконфигурировать Hibernate. Конфигурация может быть выполнена программно, я это обязательно покажу в каком-нибудь из примеров, либо в файле hibernate.cfg.xml, который должен быть доступен в classpath. Строго говоря, имя файла может быть любым, а hibernate.cfg.xml принято по умолчанию.

В конфигурации определяется метод соединения с базой данных, сама база данных, настройки Hibernate и отображения. Я задал базу H2 и настроил связь с ней. Опция  <property name="hibernate.hbm2ddl.auto">update</property> говорит Hibernate, что надо сканировать все классы, имеющие аннотацию @Entity и обновить схему таблиц базы данных сообразно этим классам. Говоря более простым языком — с этой опцией Hibernate сам создаёт таблицы для ваших классов.

Записи  <mapping class="ru.easyjava.data.hibernate.entity.Greeter"/> говорят Hibernate, что этот класс следует отображать в базу данных. В отличие от JPA Hibernate требует явного перечисления каждого класса сущности в конфигурации. Кроме того, в Hibernate поддерживаются описание отображений классов и связей между ними на чистом XML, без аннотаций вовсе.

Использование Hibernate

Использование Hibernate состоит, вообщем-то, из двух частей — сохранение объектов в базу и чтение объектов из базы:

Вначале создаётся ServiceRegistry, которое знает о всех настройках и сконфигурированных hibernate persistence units. Из ServiceRegistry создаётся уже SessionFactory. Процесс создания SessionFactory обставлен обработкой исключений, с тем, чтобы очистить ServiceRegistry в случае провала. В случае успешного создания SessionFactory она очистит за собой ServiceRegistry сама. Из SessionFactory уже открываются сессии для работы с объектами в базе данных:  Session. SessionFactory/Session могут рассматриваться как DataSource/Connection  из JDBC. Затем в Session открывается транзакция, новые объекты записываются в базу данных, транзакция подтверждается и, наконец-то, сессия закрывается.

Попробуем прочитать объекты обратно:

Опять, открываем сессию, в сессии открываем транзакцию и делаем запрос, используя HQL, язык, подобный SQL, только ориентированный на ORM. Hibernate автоматически создаёт корректный запрос, получает данные из базы данных, создаёт экземпляры объектов и наполняет их данными.

Результат исполнения это подтверждает:

Код примера доступен на github.