Удаление объекта

Открыта

Удаление с помощью метода remove()

Наконец разберем удаление объекта. В принципе удалять объекты из базы очень просто, но как говориться, есть нюансы. И таких нюансов аж шесть:

  • Удаление с помощью метода remove()
  • Удаление за компанию
  • Удаление по Orphan
  • Удаление с помощью JPQL
  • Удаление через NativeQuery
  • softDeleted()

И начнем мы с самого очевидного решения — вызова метода remove().

User user = new User();
user.setName("Колян");
session.persist(user);  //добавляем объект в базу
session.flush();
session.clear();  //закрываем сессию

user = (User) session.find(User.class, user.getId() ); //заново получаем объект из базы
session.remove(user);
session.flush();
session.clear();  //закрываем сессию

//тут объект реально удален.

Реальная операция в базе будет выполнена после вызова метода flush() или закрытия транзакции.

Каскадное удаление

Помнишь, когда мы изучали SQL, то зависимым таблицам можно было прописывать CONSTRAINT. И одна из них записывалась так:

CONSTRAINT ONDELETE REMOVE

Смысл ее был в том, что если у нас есть таблица, которая содержит дочерние сущности, то при уделении сущности-родителя нужно удалить все ее дочки.

Допустим, мы где-то храним персональную информацию пользователя и настроили CONSTRAINT в базе так, чтобы при удалении пользователя эти данные тоже удалялись. Тогда нам нужно просто удалить родительский объект и все дочерние объекты будут удалены на уровне базы:

User user = new User();
UserPrivateInfo info = new UserPrivateInfo();
user.setPrivateInfo(info);
session.persist(user);  //добавляем объект в базу, также в базу сохранится и объект info
session.flush();
session.clear();  //закрываем сессию

user = (User) session.find(User.class, user.getId() ); //заново получаем объект из базы
session.remove(user);
session.flush();
session.clear();  //закрываем сессию

//тут объекты user и info реально удалены из базы.

Удаление по Orphan

Также есть еще один тип удаления, который называют удаление по Orphan. Он чем-то похож на предыдущий вариант. Дочерняя сущность удаляется, когда разрывается ее связь с родительской сущностью. При этом родительская сущность обычно не удаляется.

Допустим, у нас есть пользователь, а у него есть список сообщений:

User user = new User();
UserMessage message = new UserMessage();
user.getMessageList().add(message);
session.persist(user);  //добавляем объект в базу, также в базу сохранится и объект message
session.flush();
session.clear();  //закрываем сессию

user = (User) session.find(User.class, user.getId() ); //заново получаем объект из базы
UserMessage message2 = user.getMessageList().get(0); //получаем сообщение пользователя
user.getMessageList().remove(message2);  //удаляем сообщение из списка
session.flush();
session.clear();  //закрываем сессию

//тут объект message2 реально удален из базы

Также есть важный нюанс, если мы хотим, чтобы Hibernate реализовывал такое поведение, его нужно явно указать при связывании двух сущностей с помощью аннотаций:

@Entity
public class User {

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List<UserMessage> messageList = new ArrayList<UserMessage>();

}

Удаление через JPQL

Еще один интересный способ удалить объект – это написать запрос на HQL (или JPQL). Только не забудь в конце вызвать метод executeUpdate(), а не то Hibernate создает read-only транзакцию и никакого удаления у тебя не выйдет.

Пример:

User user = new User();
session.persist(user);  //добавляем объект в базу
session.flush();
session.clear();  //закрываем сессию

session.createQuery("delete from User where id = :id")
   .setParameter("id", user.getId())
   .executeUpdate();

Изменение в базе никак не изменит существующие Entity-объекты.

Удаление через NativeQuery

Аналогично можно удалить и через вызов NativeQuery.

Пример:

User user = new User();
session.persist(user);  //добавляем объект в базу
session.flush();
session.clear();  //закрываем сессию

session.createNativeQuery("DELETE FROM user WHERE id = :id")
   .setParameter("id", user.getId())
   .executeUpdate();

Изменение в базе никак не затронет существующие Entity-объекты.

Мягкое удаление

Иногда вместо удаления данных в базе бывает удобно просто пометить их как удаленные. Такие данные могут потом участвовать в различных сценариях. Во-первых, такое удаление легко обратимо – строки можно опять пометить как живые.

Во-вторых, такие удаленные данные полезно “складывать в архив”, ведь бывают случаи, когда поведение сервера регулируется законодательством и тому подобное. Однако, если ты будешь помечать твои данные как удаленные, то только ты будешь знать, что они удалены. Hibernate же по-прежнему будет находить эти данные, а также использовать их при сортировке.

Поэтому создатели Hibernate придумали специальную аннотацию, с помощью которой можно было бы помечать объекты как живые. Пример:

@Entity
@Where(clause = "DELETED = 0") //во всех WHERE будет добавляться “AND DELETED = 0”
public class User {
	// маппинг полей

	@Column(name = "DELETED") // если значение в колонке DELETED == 0, то запись жива, если 1 - мертва
	private Integer deleted = 0;

	//геттеры и сеттеры

    public void softDeleted() {
    	this.deleted = 1; //помечаем запись как мертвую
    }
}

Чтобы пометить объект как удаленный нужно просто вызвать у него метод softDeleted():

User user = new User();
session.persist(user);  //добавляем объект в базу
session.flush();
session.clear();  //закрываем сессию

user = (User) session.find(User.class, user.getId() ); //заново получаем объект из базы
user.softDeleted(); //помечаем объект как удаленный
session.flush();
session.clear();  //закрываем сессию

//больше этот объект не будет находиться через Hibernate
1
Задача
Модуль 4. Работа с БД,  11 уровень4 лекция
Недоступна
Удаляем объект из БД
Удаляем объект из БД
1
Задача
Модуль 4. Работа с БД,  11 уровень4 лекция
Недоступна
Удаляем объект из БД с помощью HQL
Удаляем объект из БД с помощью HQL
Комментарии (8)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Fargys1879
Уровень 1
26 декабря 2023, 08:55
Если не ошибаюсь, то CONSTRAINT ON DELETE REMOVE относится к аннотации @OnDelete.Если не указать явно эту аннотацию, то DDL не сформирует такой констрейнт.И работает в обратную сторону, если удалить дочернюю сущность то удалится родительская через механизм самой БД
Андрей
Уровень 2
Expert
4 декабря 2023, 18:00
session.clear();  //закрываем сессию
Почему никто не обращает внимание на комментарии в коде?
session.clear(); // Очищает сессию но не закрывает
session.close(); // закрывает сессию
Дмитрий
Уровень 100
Expert
26 января 2024, 21:14
Да, очень странно. Как я понимаю, ошибка тут. Всё-таки clear() именно очищает сессию, без разрыва соединения.
Nadir Nadirli Backend Developer Expert
5 января 2023, 21:54
строчка 7, должно быть user.setDeleted();
Владислав Backend Developer
22 января 2023, 13:24
Привет, мне кажется там все правильно написано. Просто у объекта вызывается метод, который называется "softDeleted", он присваивает новое значения для поля "deleted".
Саша И.
Уровень 101
Expert
16 марта 2023, 04:25
Поле deleted имеет тип Integer, поэтому при вызове setDeleted() придётся передать аргумент. Допустимые аргументы только 0 и 1, но что будет, если передать 2? Да и зачем вообще передавать что-то кроме 0 и 1? :) Поэтому здесь всё верно - вызывается softDeleted() без аргументов.
14 мая 2023, 18:05
Тогда логичнее было сделать, чтобы метод принимал boolean
AnnaVin
Уровень 47
17 июня 2023, 13:35
Soft - мягкий, как раз как название "Мягкое удаление".