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.
Когда ты 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, приведенный выше код удаления не будет работать.