Список станів

А тепер починається найцікавіше. Ми вивчатимемо стани Entity-об'єктів. За все потрібно платити і за використання Hibernate теж. Чи не думаєш ти, що вивчення HQL – це і є така плата? Ні, життя влаштоване трохи складніше.

Якщо у тебе є якийсь Entity-об'єкт, який ти можеш зберігати в базу за допомогою Hibernate, то з погляду Hibernate цей об'єкт може мати чотири стани:

  • Transient
  • Persistent (або 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-об'єкт після від'єднання від сесії кидатиме винятки при виклику його методів.

Це найчастіша проблема у всіх новачків під час роботи з 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