Список состояний

А теперь начинается самое интересное. Мы будем изучать состояния Entity-объектов. За все нужно платить, и за использование Hibernate тоже. Не думаешь же ты, что изучение HQL – это и есть такая плата? Нет, жизнь устроена немного сложнее.

Если у тебя есть какой-то Entity-объект, который ты можешь сохранять в базу с помощью Hibernate, то с точки зрения Hibernate у этого объекта может быть четыре состояния:

  • Transient
  • Persistent (or Managed)
  • Detached
  • Removed

А чтобы тебя заинтересовать, добавлю к этой лекции вот такую картинку:

Transient

На самом деле все значительно проще чем кажется, хотя и не без нюансов. Например, каждый Entity объект, который ты создал явно с помощью Java-кода, а не загрузил из базы с помощью Hibernate, имеет статус Transient (прозрачный).


EmployeeEntity employee = new EmployeeEntity();

Статус Transient значит, что Hibernate понятия не имеет об этом объекте, и никакие действия с объектом не влияют на Hibernate, как и работа Hibernate на этот объект.

Такие объекты еще называются POJO – Plain Old Java Object. Этот термин часто используется как противоположность различным объектам с хитрым поведением. Вот помните Moc-объекты, которые создавал Mockito? Вот они не POJO.

Если некий клиентский код работает с объектом со статусом Transient, то их взаимодействие можно описать супер-простой схемой:

Persistent or Managed

Следующий самый распространенный случай – это объекты, связанные с движком Hibernate. Их статус называют Persistent (или же Managed). Способов получить объект с таким статусом ровно два:

  • Загрузить объект из Hibernate.
  • Сохранить объект в Hibernate.

Примеры:


Employee employee = session.load(Employee.class, 1);

Employee employee = new Employee ();
session.save(employee);

Такому объекту обычно соответствует какая-то запись в базе данных, у него есть ID и тому подобное. Этот объект присоединен к сессии Hibernate’а, и вообще может быть представлен не реальным объектом, а неким proxy.

Вполне реальна ситуация, когда после вызова метода session.load(), тебе вернут некий объект-заглушку (proxy), и все обращения к базе данных будут выполняться только после вызова методов этого объекта. Но о таких деталях мы поговорим немного позднее.

А взаимодействие клиентского кода и объекта в статусе Managed можно описать вот такой картинкой:

Detached

Следующее состояние – это когда объект был отсоединен от сессии. То есть когда-то объект был присоединен к сессии Hibernate, но затем сессия закрылась или транзакция завершилась, и Hibernate больше не следит за этим объектом.

Пример:


session.close();

session.evict(entity);

В первом примере сессия была закрыта. Во втором случае мы явно указали, что хотим отсоединить объект от сессии с помощью метода evict().

Новая схема взаимодействия кода и объекта будет выглядеть так:

И вот тут будет самое интересное. Если твой объект был получен из Hibernate, то велика вероятность, что тебе отдали некий proxy вместо реального объекта. И этот proxy-объект после отсоединения от сессии будет кидать исключения при вызове его методов.

Это самая частая проблема у всех новичков при работе с Hibernate. Тебе нужно точно знать в каждый момент времени ответ на такие вопросы, когда ты работаешь с Entity-объектом:

  • У тебя реальный объект или только proxy от реального объекта?
  • Ты сейчас в транзакции или нет?
  • Это read-write транзакция или read-only транзакция?
  • Объект управляется механизмом LazyLoading?
  • Какие части объекта уже загружены в память, а какие будет загружены при обращении?
  • Как твой объект соединен с зависимыми объектами?

Хорошая новость – в большинстве случаев все очевидно. Но тебе все равно нужно понимать, как это все работает под капотом. Декларативное программирование оно такое – написать код можно за 10 минут, понять почему он не работает, как надо – за 10 часов :)

Removed

И последнее состояние, которое может быть у твоего Entity-объекта – это Removed. Как ты уже наверное догадался из его названия – это состояние удаленного объекта.

Такое состояние появляется по причине того, что если ты удалишь какой-то объект из базы, то Java-объект сразу никуда не исчезнет.


Employee employee = session.load(Employee.class, 1);
//после загрузки у объекта состояние Persisted 
 
session.remove(employee);
//после удаления у объекта состояние Removed  
 
session.save(employee);
//а теперь снова Persisted 
 
session.close();
//а теперь состояние Detached