Паттерны в Hibernate

Hibernate сочетает несколько архитектурных паттернов. Название помогает понять ответственность, но реализация содержит дополнительные правила ORM.

Data Mapper

Mapper переносит состояние между объектной и реляционной моделями, не заставляя entity самостоятельно выполнять SQL.

Entity ↔ Persistence Context / Mapper ↔ Database row

В отличие от Active Record, entity обычно не содержит save() или delete() для обращения к БД.

См. JPA Hibernate ORM и Маппинг Hibernate.

Identity Map

Внутри одного Persistence Context одной database identity соответствует один managed Java-object:

Order first = entityManager.find(Order.class, 42L);
Order second = entityManager.find(Order.class, 42L);
 
assert first == second;

Это позволяет dirty checking сравнивать изменения управляемого экземпляра и предотвращает две противоречивые копии одной строки внутри context.

Unit of Work

Persistence Context накапливает изменения managed entities и синхронизирует их с БД при flush. Код меняет объект, а SQL может выполниться позже.

@Transactional
public void rename(long id, String name) {
    Order order = entityManager.find(Order.class, id);
    order.rename(name);
    // UPDATE сформируется при flush/commit
}

Flush не равен commit: SQL может уже уйти в БД, но транзакция ещё способна откатиться.

Proxy

Proxy позволяет представить ленивую ссылку без немедленной загрузки. Обращение к данным может выполнить SQL.

Ограничения:

  • context должен быть доступен;
  • метод не должен быть final, если proxy строится через subclass;
  • equals, logging и serialization способны случайно инициировать загрузку;
  • proxy не решает N+1.

Factory

EntityManagerFactory создаёт EntityManager. В native Hibernate аналогичную роль играет SessionFactory.

Factory тяжёлая и обычно живёт всё приложение. EntityManager/Session короткоживущие и не должны совместно использоваться потоками.

Repository и DAO

Repository выражает операции над aggregate/domain objects. DAO ближе к persistence API и таблицам. В маленьком приложении реализации могут выглядеть одинаково, но намерение различается.

Вопросы для собеседования

  1. Какой паттерн даёт один объект на identity внутри context? Identity Map.
  2. Почему UPDATE выполняется без save? Managed entity участвует в Unit of Work и dirty checking.
  3. Является ли lazy proxy загруженной entity? Он представляет ссылку, а состояние может быть ещё не загружено.
  4. Можно ли разделять EntityManager между потоками? Нет.
  5. Flush фиксирует транзакцию? Нет.

Связанные заметки