Hello, JPA (и Hibernate)

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

Более того, таких решений настолько много, что в Java даже появился стандартный интерфейс, который направлен на стандартизацию ORM продуктов. Этот интерфейс называется — Java persistence API и описывает требования к объектам, для сохранения их в базах данных, интерфейсы для сохранения объектов и интерфейсы для получения объектов из БД.

Сам JPA является лишь описательным стандартом и пачкой аннотаций, поэтому у него есть несколько реализаций. Одна из самых популярных и, в то же время, одна из не самых стандартных реализаций, называется Hibernate.

Попробуем использовать их вместе.

Подготовка

Как всегда, начнём с пустого maven проекта, в который добавим реализацию JPA и встраиваемую базу :

Кроме того я добавил project lombok для упрощения кода и библиотеки модульного тестирования.

Entity

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

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

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

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

Настройка JPA

Вторым важным шагом после создания объектов для данных будет настройка JPA. Для этого создадим файл META-INF/persistence.xml и напишем в него конфигурацию JPA.

Главный и обязательный тег в этом в файле:  <persistence-unit name="ru.easyjava.data.jpa.hibernate">, который задаёт имя конкретного persistence unit (а их может быть несколько) и его опции.

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

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

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

Вначале создаётся EntityManagerFactory  для конкретного persistence unit, заданного своим именем. Из фабрики по мере надобности получаются EntityManager , с которыми уже и работают. Можно рассматривать их как аналог DataSource/Connection. Затем открывается транзакция, заранее созданные объекты сохраняются в базу, транзакция подтверждается и EntityManager  закрывается. И ни капли SQL!

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

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

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

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