JPA 2.0 orphanRemoval=true VS при удалении каскада

Я немного запутался в JPA 2.0 orphanRemoval приписывать.

Я думаю, что вижу, что это необходимо, когда я использую инструменты генерации БД моего провайдера JPA для создания базовой базы данных DDL, чтобы иметь ON DELETE CASCADE на конкретное отношение.

Однако, если БД существует и уже имеет ON DELETE CASCADE в отношении, это не достаточно для каскадного удаления надлежащим образом? Что это orphanRemoval делать дополнительно?

ура

7 ответов

Решение

orphanRemoval не имеет ничего общего с ON DELETE CASCADE,

orphanRemoval это совершенно специфичная для ORM вещь. Он помечает "дочернюю" сущность для удаления, когда на нее больше нет ссылок из "родительской" сущности, например, когда вы удаляете дочернюю сущность из соответствующей коллекции родительской сущности.

ON DELETE CASCADE это специфичная для базы данных вещь, она удаляет "дочернюю" строку в базе данных при удалении "родительской" строки.

Пример взятой формы здесь:

Когда Employee объект сущности удален, операция удаления каскадно связана с Address объект В этой связи, orphanRemoval=true а также cascade=CascadeType.REMOVE идентичны, и если orphanRemoval=true указано, CascadeType.REMOVE избыточно

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

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

  • Если только cascade=CascadeType.REMOVE указано, автоматическое действие не предпринимается, поскольку отключение отношения не является операцией удаления.

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

Я надеюсь, что это делает это более ясным.

В тот момент, когда вы удаляете дочернюю сущность из коллекции, вы также удаляете эту дочернюю сущность из БД. orphanRemoval также подразумевает, что вы не можете сменить родителей; если есть отдел, в котором есть сотрудники, после того, как вы удалите этого сотрудника, чтобы перевести его в другой отдел, вы по неосторожности удалите этого сотрудника из БД при очистке / фиксации (что произойдет раньше). Мораль состоит в том, чтобы установить для orphanRemoval значение true, если вы уверены, что дети этого родителя не будут мигрировать к другому родителю на протяжении всего своего существования. Включение orphanRemoval также автоматически добавляет REMOVE в каскадный список.

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

Переходы между состояниями объекта

JPA переводит переходы состояния объекта в операторы SQL, такие как INSERT, UPDATE или DELETE.

Переходы состояний объекта JPA

Когда ты persist сущности, вы планируете выполнение инструкции INSERT, когда EntityManager сбрасывается автоматически или вручную.

когда ты remove сущности, вы планируете оператор DELETE, который будет выполнен при сбросе контекста постоянства.

Каскадные переходы состояний объекта

Для удобства JPA позволяет распространять переходы состояний сущностей от родительских сущностей к дочерним.

Итак, если у вас есть родитель Post субъект, имеющий @OneToManyассоциация сPostComment дочерняя сущность:

В comments коллекция в Post сущность отображается следующим образом:

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

CascadeType.ALL

В cascade атрибут сообщает поставщику JPA передать переход состояния объекта от родительского Post сущность для всех PostComment сущности, содержащиеся в comments коллекция.

Итак, если вы удалите Post организация:

Post post = entityManager.find(Post.class, 1L);
assertEquals(2, post.getComments().size());

entityManager.remove(post);

Провайдер JPA собирается удалить PostComment сущность сначала, и когда все дочерние сущности будут удалены, он удалит Post также сущность:

DELETE FROM post_comment WHERE id = 1
DELETE FROM post_comment WHERE id = 2

DELETE FROM post WHERE id = 1

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

Когда вы устанавливаете orphanRemoval приписывать true, поставщик JPA планирует запланировать remove операция, когда дочерняя сущность удаляется из коллекции.

Итак, в нашем случае

Post post = entityManager.find(Post.class, 1L);
assertEquals(2, post.getComments().size());

PostComment postComment = post.getComments().get(0);
assertEquals(1L, postComment.getId());

post.getComments().remove(postComment);

Провайдер JPA собирается удалить связанный post_comment запись с PostComment объект больше не упоминается в comments коллекция:

DELETE FROM post_comment WHERE id = 1

НА КАСКАДЕ УДАЛЕНИЯ

В ON DELETE CASCADE определяется на уровне FK:

ALTER TABLE post_comment 
ADD CONSTRAINT fk_post_comment_post_id 
FOREIGN KEY (post_id) REFERENCES post 
ON DELETE CASCADE;

Как только вы это сделаете, если вы удалите post ряд:

DELETE FROM post WHERE id = 1

Все связанные post_commentобъекты автоматически удаляются ядром базы данных. Однако это может быть очень опасной операцией, если вы по ошибке удалите корневой объект.

Вывод

Преимущество JPA cascade а также orphanRemovalВариантов является то, что вы также можете воспользоваться оптимистической блокировкой для предотвращения потери обновлений.

Если вы используете каскадный механизм JPA, вам не нужно использовать уровень DDL. ON DELETE CASCADE, что может быть очень опасной операцией, если вы удалите корневой объект, у которого есть много дочерних объектов на нескольких уровнях.

Подробнее об этой теме читайте в этой статье.

Эквивалентное отображение JPA для DDL ON DELETE CASCADE является cascade=CascadeType.REMOVE, Удаление без учета означает, что зависимые объекты удаляются, когда связь с их "родительским" объектом уничтожается. Например, если ребенок удален из @OneToMany отношения без явного удаления его в сущности менеджера.

Разница в том, что:
- orphanRemoval = true: дочерний объект удаляется, когда на него больше нет ссылок (его родитель не может быть удален).
- CascadeType.REMOVE: "Дочерняя" сущность удаляется только тогда, когда ее "Родитель" удаляется.

@GaryK ответ абсолютно отличный, я потратил час на поиски объяснения orphanRemoval = true против CascadeType.REMOVE и это помогло мне понять.

Подводя итог: orphanRemoval = true работает так же, как CascadeType.REMOVE ТОЛЬКО ЕСЛИ мы удаляем объект (entityManager.delete(object)) и мы хотим, чтобы дочерние объекты также были удалены.

В совершенно другом положении, когда мы выбираем такие данные, как List<Child> childs = object.getChilds() а потом убрать ребенка (entityManager.remove(childs.get(0)) с помощью orphanRemoval=true будет вызывать эту сущность, соответствующую childs.get(0) будут удалены из базы данных.

Удаление сирот имеет тот же эффект, что и ON DELETE CASCADE в следующем сценарии:- Допустим, у нас есть простое отношение "многие к одному" между сущностью студента и сущностью-гидом, где многие ученики могут быть сопоставлены с одним и тем же руководством, и в базе данных есть отношение внешнего ключа между таблицей Student и Guide так, что для таблицы Student есть id_guide в виде FK.

    @Entity
    @Table(name = "student", catalog = "helloworld")
    public class Student implements java.io.Serializable {
     @Id
     @GeneratedValue(strategy = IDENTITY)
     @Column(name = "id")
     private Integer id;

    @ManyToOne(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
    @JoinColumn(name = "id_guide")
    private Guide guide;

// родительский объект

    @Entity
    @Table(name = "guide", catalog = "helloworld")
    public class Guide implements java.io.Serializable {

/**
 * 
 */
private static final long serialVersionUID = 9017118664546491038L;

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Integer id;

@Column(name = "name", length = 45)
private String name;

@Column(name = "salary", length = 45)
private String salary;


 @OneToMany(mappedBy = "guide", orphanRemoval=true) 
 private Set<Student> students = new  HashSet<Student>(0);

В этом сценарии взаимосвязь такова, что объект учащегося является владельцем отношения, и поэтому нам необходимо сохранить объект учащегося, чтобы сохранить весь граф объекта, например

    Guide guide = new Guide("John", "$1500");
    Student s1 = new Student(guide, "Roy","ECE");
    Student s2 = new Student(guide, "Nick", "ECE");
    em.persist(s1);
    em.persist(s2);

Здесь мы отображаем одно и то же руководство с двумя разными объектами учеников, и поскольку используется CASCADE.PERSIST, граф объектов будет сохранен, как показано ниже в таблице базы данных (в моем случае MySql)

СТУДЕНЧЕСКИЙ стол:-

ID Name Dept Id_Guide

1 Рой ЕЭК 1

2 ник ECE    1

Таблица руководств:-

ID NAME Зарплата

1 Джон 1500 долларов

и теперь, если я хочу удалить одного из студентов, используя

      Student student1 = em.find(Student.class,1);
      em.remove(student1);

и когда запись об ученике удаляется, соответствующая запись об упорядочении также должна быть удалена, вот где атрибут CASCADE.REMOVE в сущности "Студент" входит в изображение и что он делает: он удаляет учащегося с идентификатором 1, а также соответствующий объект-указатель (идентификатор) 1). Но в этом примере есть еще один объект ученика, который сопоставлен с той же направляющей записью, и если мы не используем атрибут orphanRemoval = true в объекте Guide, приведенный выше код удаления не будет работать.

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