В чем разница между каскадным и бесхозным удалением из БД?

В чем разница между

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

а также

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

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

3 ответа

Решение

Отсюда:-

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

Пометка ссылочного поля с помощью 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, отключенный экземпляр адреса автоматически удаляется. Это полезно для очистки зависимых объектов (например, адреса), которые не должны существовать без ссылки от объекта-владельца (например, сотрудника).
  • Если указано только cascade=CascadeType.REMOVE, автоматическое действие не выполняется, поскольку отключение отношения не является удалением
    операция.

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

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

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

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

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

orphanRemoval - это концепция ORM, которая сообщает, является ли ребенок сиротой. его также следует удалить из базы данных.

Ребенок становится сиротой, когда к нему нельзя получить доступ от его родителя. Например, если мы удалим obj по индексу i (используя myChildernList.remove(i)) или установим его в null или заменим его новым (staffList.set(i, newChild)), тогда родитель больше не сможет получить доступ к этому дочернему элементу. и ребенок осиротел, поэтому ребенок также обречен на удаление из базы данных (это душераздирающе:()

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

Поскольку этот вопрос очень распространен, этот ответ основан на статье, которую я написал в своем блоге.

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

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

Фактически разница заключается в том, пытаетесь ли вы обновить данные (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)

Другие вопросы по тегам