Что делает метод detach в Hibernate и что происходит после его вызова
Что проверяют
Понимаете ли вы жизненный цикл сущности и роль контекста персистентности. Вопрос отделяет тех, кто знает четыре состояния и умеет объяснить, где Hibernate следит за объектом, а где он просто набор полей в памяти, от тех, кто знает только save и findById.
Как отвечать
Сущность живёт в одном из состояний: transient, managed, detached, removed. entityManager.detach(entity) (родной аналог в Hibernate это session.evict) убирает объект из контекста персистентности. После вызова:
- автоматическое отслеживание изменений прекращается, правки в полях в базу не поедут ни на flush, ни на commit;
- незафлашенные изменения именно этой сущности теряются, detach не выполняет flush перед отсоединением;
- ленивые связи, не подгруженные до вызова, при обращении дадут
LazyInitializationException; - объект остаётся валидным в памяти, его можно отдать в другой слой или сериализовать.
Вернуть в контекст помогает merge: он копирует состояние в управляемый экземпляр и возвращает его, работать дальше надо с возвращённым значением. Весь контекст очищает clear(), а закрытие сессии отсоединяет всё автоматически. Практический сценарий: пакетная обработка, где контекст чистят порциями, чтобы он не разрастался и не съедал память.
Где спотыкаются
Думают, что merge прикрепляет переданный объект. Он возвращает другой экземпляр, а исходный так и остаётся отсоединённым, поэтому правильная форма записи это entity = em.merge(entity). Нативный update для той же задачи считается устаревшим.
Ловят LazyInitializationException в контроллере и лечат его включением отложенного закрытия сессии на весь запрос. Правильнее подгрузить нужное внутри транзакции через fetch join или собрать DTO, иначе ленивые запросы расползаются по слою представления.
Путают detach с удалением. Отсоединение не трогает строку в базе вообще; за удаление отвечает remove. И обратная сторона: detach не откатывает уже выполненный flush, если SQL ушёл в базу, его отменит только откат транзакции.