В чем разница между CascadeType.REMOVE и orphanRemoval в JPA?

103

какая разница между

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

и

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

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

rand0m86
источник
Удаление сиротства означает, что зависимые объекты удаляются, когда разрушается связь с их «родительским» объектом.
Рахул Трипати,
1
Написал тестовый пример, который может проиллюстрировать концепцию.
Мартин Андерссон

Ответы:

153

От сюда : -

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

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

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

Удаление сирот

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

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

РАЗНИЦА: -

Разница между двумя настройками заключается в реакции на разрыв отношений. Например, например, при установке в поле адреса значения null или другого объекта Address.

  • Если указано orphanRemoval = true , отключенный экземпляр Address автоматически удаляется. Это полезно для очистки зависимых объектов (например, Address), которые не должны существовать без ссылки от объекта-владельца (например, Employee).
  • Если указан только cascade = CascadeType.REMOVE , автоматические действия не выполняются, поскольку разрыв связи не является
    операцией удаления .
Рахул Трипати
источник
87

Простой способ понять разницу между CascadeType.REMOVEиorphanRemoval=true .

Для удаления сирот: если вы вызываете setOrders(null), связанные Orderобъекты будут автоматически удалены в базе данных.

Для удаления каскада: если вы вызываете setOrders(null), связанные Orderобъекты НЕ будут автоматически удалены из базы данных.

учиться
источник
2
remove === delete
Абдул
10

Предположим, у нас есть дочерняя и родительская сущности. У родителя может быть несколько детей.

@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)

garg10май
источник
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

Для получения дополнительных сведений по этой теме ознакомьтесь также с этой статьей .

Влад Михалча
источник