1. Видалення за допомогою методу 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() або закриття транзакції.

2. Каскадне видалення

Пам'ятаєш, коли ми вивчали 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 реально видалені з бази.

3. Видалення за 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>();
 
}

4. Видалення через 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-об'єкти.

5. Видалення через 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-об'єктів.

6. М'яке видалення

Іноді замість видалення даних у базі буває зручно просто позначити їх як видалені. Такі дані можуть потім брати участь у різних сценаріях. По-перше, таке видалення легко оборотне — рядки можна знову позначити як живі.

По-друге, такі видалені дані корисно “складати до архіву”, адже бувають випадки, коли поведінка сервера регулюється законодавством тощо. Однак, якщо ти позначатимеш твої дані як видалені, то тільки ти знатимеш, що вони видалені. 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