У чому різниця між CascadeType.REMOVE та orphanRemoval у JPA?


100

Яка різниця між

@OneToMany(cascade=REMOVE, mappedBy="customer")
public List<Order> getOrders() { ... }

і

@OneToMany(mappedBy="customer", orphanRemoval="true")
public List<Order> getOrders() { ... }

Цей приклад з Java EE Tutorial, але я все ще не розумію деталей.


Вилучення сиріт означає, що залежні сутності видаляються, коли відношення до їхньої "батьківської" сутності руйнується.
Рахул Трипаті

1
Написав тест, який може проілюструвати концепцію.
Мартін Андерссон,

Відповіді:


152

Від сюди : -

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

Позначення поля посилання за допомогою CascadeType.REMOVE (або CascadeType.ALL, що включає ВИДАЛЕННЯ) означає, що операції видалення повинні автоматично каскадуватися до об'єктів сутності, на які посилається це поле (на кілька об'єктів сутності може посилатися поле збору):

@Entity
class Employee {
     :
    @OneToOne(cascade=CascadeType.REMOVE)
    private Address address;
     :
}

Видалення сироти

JPA 2 підтримує додатковий і більш агресивний режим каскадного видалення, який можна вказати за допомогою елемента orphanRemoval в анотаціях @OneToOne та @OneToMany:

@Entity
class Employee {
     :
    @OneToOne(orphanRemoval=true)
    private Address address;
     :
}

РІЗНИЦЯ: -

Різниця між двома параметрами полягає у реакції на розрив стосунків. Наприклад, наприклад, при встановленні поля адреси на нуль або на інший об'єкт Адреса.

  • Якщо вказано orphanRemoval = true, екземпляр адреси, що відключено, автоматично видаляється. Це корисно для очищення залежних об'єктів (наприклад, адреси), які не повинні існувати без посилання на об'єкт власника (наприклад, працівник).
  • Якщо вказано лише cascade = CascadeType.REMOVE , автоматичні дії не виконуються, оскільки відключення відносин не є
    операцією видалення .

87

Простий спосіб зрозуміти різницю між CascadeType.REMOVEі orphanRemoval=true.

Для видалення сиріт: якщо ви викликаєте setOrders(null), пов’язані Orderоб’єкти буде автоматично видалено в db.

Для видалення каскаду: Якщо ви викликаєте setOrders(null), пов’язані Orderсутності НЕ будуть автоматично видалятися в db.


2
видалити === видалити
Абдул

9

Припустимо, у нас є дочірня сутність і батьківська сутність. У одного з батьків може бути кілька дітей.

@Entity
class parent {
  //id and other fields
 @OneToMany (orphanRemoval = "true",cascade = CascadeType.REMOVE)
   Set<Person> myChildern;
}

OrphanRemoval - це концепція ORM, яка вказує, чи осиротіла дитина. його також слід видалити з бази даних.

Дитина осиротіла, коли до неї неможливо отримати доступ від батьків. Наприклад, якщо ми видалимо набір об’єктів Person (встановивши його на порожній набір) або замінимо новим, тоді батьки більше не матимуть доступу до дітей у старому наборі, а діти осиротіли, тому діти приречені бути також видалено в базі даних.

CascadeType.REMOVE - це концепція рівня бази даних, і вона повідомляє, якщо батьківський вилучено, усі пов’язані з ним записи у дочірній таблиці слід видалити.


2

Практично різниця полягає в тому, чи намагаєтесь ви оновити дані (PATCH) або повністю замінити дані (PUT)

Скажімо, якщо ви видалите те, customerчим користуєтеся, cascade=REMOVEви також видалите замовлення клієнтів, які здаються цілеспрямованими та корисними.

@OneToMany(cascade=REMOVE, mappedBy="customer")
public List<Order> getOrders() { ... }

Тепер припустимо, що ви оновлюєте customerз orphanRemoval="true"нею будуть видалені всі попередні замовлення і замінити їх в комплект поставки. ( PUTз точки зору REST API)

@OneToMany(mappedBy="customer", orphanRemoval="true")
public List<Order> getOrders() { ... }

Без orphanRemovalстарих наказів трималися б. ( PATCHз точки зору REST API)


1

Оскільки це питання дуже поширене, ця відповідь базується на цій статті, яку я написав у своєму блозі.

CascadeType.REMOVE

CascadeType.REMOVEСтратегія, яку ви можете налаштувати в явному вигляді:

@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.REMOVE
)
private List<PostComment> comments = new ArrayList<>();

або неявно успадкувати його від CascadeType.ALLстратегії:

@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.ALL
)
private List<PostComment> comments = new ArrayList<>();

дозволяє розповсюдити removeоперацію від батьківської сутності до її дочірніх сутностей.

Отже, якщо ми дістаємо батьківську Postсутність разом з її commentsколекцією та видаляємо postсутність:

Post post = entityManager.createQuery("""
    select p
    from Post p
    join fetch p.comments
    where p.id = :id
    """, Post.class)
.setParameter("id", postId)
.getSingleResult();

entityManager.remove(post);

Hibernate виконає три оператори видалення:

DELETE FROM post_comment 
WHERE id = 2

DELETE FROM post_comment 
WHERE id = 3

DELETE FROM post 
WHERE id = 1

У PostCommentдочірніх об'єктах були видалені з-за CascadeType.REMOVEстратегії, яка діяла , як якщо б ми прибрали дочірні об'єкти , а також.

Стратегія вилучення сиріт

Стратегія вилучення сиріт, яку потрібно встановити через orphanRemovalатрибут:

@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();

дозволяє видалити рядок дочірньої таблиці після видалення дочірньої сутності з колекції.

Отже, якщо ми завантажимо Postсутність разом із її commentsколекцією та видалимо першу PostCommentз commentsколекції:

Post post = entityManager.createQuery("""
    select p
    from Post p
    join fetch p.comments c
    where p.id = :id
    order by p.id, c.id
    """, Post.class)
.setParameter("id", postId)
.getSingleResult();

post.remove(post.getComments().get(0));

Hibernate виконує оператор DELETE для відповідного post_commentрядка таблиці:

DELETE FROM post_comment 
WHERE id = 2

Докладніше про цю тему також ознайомтесь із цією статтею .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.