Короткий ответ
В Hibernate сущность проходит четыре состояния. Transient — объект только что создан через new, не связан с Session и не имеет соответствия в базе. Persistent — связан с открытой Session и имеет запись в БД, изменения отслеживаются. Detached — был persistent, но Session закрыта или вызваны evict()/detach(). Removed — помечен на удаление и будет удалён при синхронизации.
Как это работает подробнее
Переходы выполняются операциями Session: save()/persist() переводят transient в persistent, закрытие сессии или evict() — в detached, а update(), saveOrUpdate() или merge() возвращают объект в persistent. От состояния зависит, какие операции Hibernate выполнит при коммите и какие исключения могут возникнуть.
- Transient: объект не управляется Hibernate, записи в базе ещё нет.
- Persistent: работает dirty checking — Hibernate сам синхронизирует изменения с БД при flush или commit, явный
update()не нужен. - Detached: изменения больше не отслеживаются; чтобы сохранить их, объект нужно присоединить заново.
- Removed: объект удаляется из базы при flush, после чего Hibernate им больше не управляет.
- Пример:
session.get(...)даёт persistent; после закрытия сессии объект становится detached;merge()в новой сессии снова делает его persistent.
Пример кода
StateTransitionsDemo.java
Employee emp = new Employee(); // transient
session.persist(emp); // persistent
session.evict(emp); // detached
session.close();
try (Session s2 = sessionFactory.openSession()) {
s2.beginTransaction();
Employee managed = (Employee) s2.merge(emp); // снова persistent
s2.remove(managed); // removed
s2.getTransaction().commit(); // удаление уходит в БД
}Что использовать на практике
- Работайте с сущностью в пределах одной открытой Session, чтобы оставаться в состоянии persistent.
- Для передачи сущности между слоями используйте detached-объект и
merge()в новой транзакции. - После закрытия сессии не рассчитывайте на автосинхронизацию: изменения detached-объекта нужно явно присоединять.
Подводные камни
- merge возвращает новый управляемый экземпляр — дальше работать нужно с ним, а не со старым объектом.
- Доступ к ленивым связям detached-объекта вне сессии приводит к LazyInitializationException.
- Попытка вставить detached-объект через
save()вместоmerge()может дать дубликат записи.
Как отвечать на собеседовании
- Перечислите четыре состояния: transient, persistent, detached, removed → для каждого объясните, связан ли объект с Session и есть ли запись в БД → покажите переходы операциями: save/persist, evict/detach, close, update/saveOrUpdate/merge, remove → упомяните dirty checking в persistent → сделайте вывод, что от состояния зависят действия Hibernate при flush и commit.