В каких состояниях может быть entity в persistence context?
Что проверяют
Понимаете ли вы жизненный цикл сущности в JPA и Hibernate настолько, чтобы объяснить, почему изменение сохранилось без вызова save() или, наоборот, не сохранилось после него. Следом спрашивают про merge() и LazyInitializationException.
Как отвечать
Состояний четыре:
- transient (new): объект создан через
new, контекст о нём не знает, строки в базе нет; - managed (persistent): объект в persistence context. Сюда попадают после
persist(),find(), JPQL-запроса. Для него работает dirty checking: изменения полей сами уходят в базу приflush, перед коммитом или перед запросом, которому нужны свежие данные; - detached: строка в базе есть, но контекст объект больше не отслеживает. Причины:
detach(),clear(),close()или конец транзакции, если контекст привязан к ней, как в Spring по умолчанию; - removed: после
remove()объект помечен на удаление,DELETEвыполнится приflush. Повторныйpersist()возвращает его в managed.
Ключевые переходы. merge() не присоединяет переданный объект, а копирует его состояние в managed-экземпляр и возвращает этот экземпляр. remove() для detached-объекта бросает IllegalArgumentException, а persist() для detached по спецификации заканчивается EntityExistsException (сразу или при flush).
Хорошо связать с Spring Data: save() вызывает persist(), если isNew() возвращает true (обычно это id, равный null), и merge() в остальных случаях. Поэтому при заданных вручную идентификаторах save() делает лишний SELECT.
Где спотыкаются
Продолжают менять объект, переданный в merge(), а не возвращённый результат. Изменения не сохраняются.
Вызывают repository.save() для управляемой сущности внутри @Transactional «на всякий случай» и не могут объяснить dirty checking. Или меняют сущность вне транзакции и ждут, что она сохранится.
Получают LazyInitializationException на detached-сущности и лечат его FetchType.EAGER или надеются на spring.jpa.open-in-view, который Spring Boot включает по умолчанию. Правильнее загрузить нужное в пределах транзакции через JOIN FETCH или @EntityGraph.