Список состояний
А теперь начинается самое интересное. Мы будем изучать состояния 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
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ