Автор оригинала: Michał Dąbrowski.
1. введение
Hibernate-это удобная платформа для управления постоянными данными, но понимание того, как она работает внутри, иногда может быть сложным.
В этом уроке мы узнаем о состояниях объектов и о том, как перемещаться между ними. Мы также рассмотрим проблемы, с которыми мы можем столкнуться с обособленными сущностями, и способы их решения.
2. Сеанс гибернации
Интерфейс Session является основным инструментом, используемым для связи с Hibernate. Он предоставляет API, позволяющий нам создавать, читать, обновлять и удалять постоянные объекты. Сеанс | имеет простой жизненный цикл. Мы открываем его, выполняем некоторые операции, а затем закрываем.
Когда мы работаем с объектами во время сеанса , они привязываются к этому сеансу . Изменения, которые мы вносим, обнаруживаются и сохраняются при закрытии. После закрытия Hibernate разрывает связи между объектами и сеансом.
3. Состояния объектов
В контексте Hibernate Session объекты могут находиться в одном из трех возможных состояний: переходном, постоянном или отсоединенном.
3.1. Переходные процессы
Объект, который мы не подключили ни к одному сеансу , находится в переходном состоянии. Поскольку он никогда не сохранялся, он не имеет никакого представления в базе данных. Поскольку ни один сеанс не знает об этом, он не будет сохранен автоматически.
Давайте создадим пользовательский объект с помощью конструктора и подтвердим, что он не управляется сеансом:
Session session = openSession(); UserEntity userEntity = new UserEntity("John"); assertThat(session.contains(userEntity)).isFalse();
3.2. Постоянный
Объект, который мы связали с сеансом , находится в постоянном состоянии. Мы либо сохранили его, либо прочитали из контекста сохранения, поэтому он представляет некоторую строку в базе данных.
Давайте создадим объект, а затем используем метод persist , чтобы сделать его постоянным:
Session session = openSession(); UserEntity userEntity = new UserEntity("John"); session.persist(userEntity); assertThat(session.contains(userEntity)).isTrue();
В качестве альтернативы мы можем использовать метод save . Разница в том, что метод persist просто сохранит объект, а метод save дополнительно сгенерирует его идентификатор, если это необходимо.
3.3. Отдельно стоящий
Когда мы закрываем сеанс , все объекты внутри него отсоединяются. Хотя они по-прежнему представляют строки в базе данных, они больше не управляются никаким сеансом :
session.persist(userEntity); session.close(); assertThat(session.isOpen()).isFalse(); assertThatThrownBy(() -> session.contains(userEntity));
Далее мы узнаем, как сохранять временные и обособленные объекты.
4. Сохранение и повторное подключение объекта
4.1. Сохранение переходного объекта
Давайте создадим новую сущность и сохраним ее в базе данных. Когда мы впервые создадим объект, он будет находиться в переходном состоянии.
Чтобы сохранить нашу новую сущность, мы будем использовать метод persist/|:
UserEntity userEntity = new UserEntity("John"); session.persist(userEntity);
Теперь мы создадим еще один объект с тем же идентификатором, что и первый. Этот второй объект является временным , потому что он еще не управляется никаким сеансом , но мы не можем сделать его постоянным с помощью метода persist/|. Он уже представлен в базе данных, поэтому на самом деле он не является новым в контексте уровня сохраняемости.
Вместо этого мы будем использовать метод merge , чтобы обновить базу данных и сделать объект постоянным :
UserEntity onceAgainJohn = new UserEntity("John"); session.merge(onceAgainJohn);
4.2. Сохранение обособленного объекта
Если мы закроем предыдущий сеанс , наши объекты будут находиться в обособленном состоянии. Как и в предыдущем примере, они представлены в базе данных, но в настоящее время они не управляются никаким сеансом . Мы можем снова сделать их постоянными, используя метод merge :
UserEntity userEntity = new UserEntity("John"); session.persist(userEntity); session.close(); session.merge(userEntity);
5. Вложенные сущности
Все усложняется, когда мы рассматриваем вложенные сущности. Допустим, наша пользовательская сущность также будет хранить информацию о своем менеджере:
public class UserEntity { @Id private String name; @ManyToOne private UserEntity manager; }
Когда мы сохраняем эту сущность, нам нужно думать не только о состоянии самой сущности, но и о состоянии вложенной сущности. Давайте создадим постоянную пользовательскую сущность, а затем установим ее менеджер:
UserEntity userEntity = new UserEntity("John"); session.persist(userEntity); UserEntity manager = new UserEntity("Adam"); userEntity.setManager(manager);
Если мы попытаемся обновить его сейчас, мы получим исключение:
assertThatThrownBy(() -> { session.saveOrUpdate(userEntity); transaction.commit(); });
java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.baeldung.states.UserEntity.manager -> com.baeldung.states.UserEntity
Это происходит потому, что Hibernate не знает, что делать с временной вложенной сущностью.
5.1. Сохранение Вложенных Сущностей
Одним из способов решения этой проблемы является явное сохранение вложенных сущностей:
UserEntity manager = new UserEntity("Adam"); session.persist(manager); userEntity.setManager(manager);
Затем, после совершения транзакции, мы сможем получить правильно сохраненную сущность:
transaction.commit(); session.close(); Session otherSession = openSession(); UserEntity savedUser = otherSession.get(UserEntity.class, "John"); assertThat(savedUser.getManager().getName()).isEqualTo("Adam");
5.2. Каскадные операции
Переходные вложенные сущности могут сохраняться автоматически, если мы правильно настроим свойство отношения cascade в классе сущностей:
@ManyToOne(cascade = CascadeType.PERSIST) private UserEntity manager;
Теперь, когда мы сохраняем объект, эта операция будет каскадирована на все вложенные объекты:
UserEntityWithCascade userEntity = new UserEntityWithCascade("John"); session.persist(userEntity); UserEntityWithCascade manager = new UserEntityWithCascade("Adam"); userEntity.setManager(manager); // add transient manager to persistent user session.saveOrUpdate(userEntity); transaction.commit(); session.close(); Session otherSession = openSession(); UserEntityWithCascade savedUser = otherSession.get(UserEntityWithCascade.class, "John"); assertThat(savedUser.getManager().getName()).isEqualTo("Adam");
6. Резюме
В этом уроке мы более подробно рассмотрели, как работает Hibernate Session в отношении состояния объекта. Затем мы рассмотрели некоторые проблемы, которые он может создать, и способы их решения.
Как всегда, исходный код доступен на GitHub .