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

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

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

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

А щоб тебе зацікавити, додам до цієї лекції ось таку картинку:

2. Transient

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


EmployeeEntity employee = new EmployeeEntity();

Статус Transient означає, що Hibernate жодного поняття не має про цей об'єкт, і жодні дії з об'єктом не впливають на Hibernate, як і робота Hibernate на цей об'єкт.

Такі об'єкти ще називаються POJO — Plain Old Java Object. Цей термін часто використовується як протилежність до різних об'єктів з хитрою поведінкою. Пам'ятаєш Mock-об'єкти, які створював Mockito? Ось вони не POJO.

Якщо клієнтський код працює з об'єктом зі статусом Transient, їх взаємодію можна описати суперпростою схемою:

3. 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 можна описати ось такою картинкою:

4. Detached

Наступний стан — це коли об'єкт від'єднано від сесії. Тобто, колись об'єкт був приєднаний до сесії Hibernate, але потім сесія закрилася або транзакція завершилася, і Hibernate більше не стежить за цим об'єктом.

Приклад:


session.close();

session.evict(entity);

У першому прикладі сесія була закрита. У другому випадку ми явно вказали, що хочемо від'єднати об'єкт від сесії за допомогою методу evict().

Нова схема взаємодії коду та об'єкта виглядатиме так:

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

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

  • Ти маєш реальний об'єкт чи лише proxy від реального об'єкта?
  • Ти зараз у транзакції чи ні?
  • Це read-write транзакція чи read-only транзакція?
  • Об'єкт керується механізмом LazyLoading?
  • Які частини об'єкта вже завантажені до пам'яті, а які будуть завантажені під час звернення?
  • Як твій об'єкт пов'язаний із залежними об'єктами?

Хороша новина: здебільшого все очевидно. Але тобі все одно треба розуміти, як усе це працює під капотом. Декларативне програмування воно таке: написати код можна за 10 хвилин, а зрозуміти, чому він не працює, як треба — за 10 годин :)

5. Removed

І останній стан, який може бути у твого Entity-об'єкта — це Removed. Як ти вже, напевно, здогадуєшся з його назви, це стан видаленого об'єкта.

Такий стан з'являється через те, що якщо ти видалиш якийсь об'єкт з бази, Java-об'єкт одразу нікуди не зникне.


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