Что означает CascadeType.ALL для ассоциации @ManyToOne JPA

210

Я думаю, что я неправильно понял значение каскадирования в контексте @ManyToOneотношений.

Дело:

public class User {

   @OneToMany(fetch = FetchType.EAGER)
   protected Set<Address> userAddresses;

}

public class Address {

   @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
   protected User addressOwner;

}

В чем смысл cascade = CascadeType.ALL? Например, если я удаляю определенный адрес из базы данных, как тот факт, что я добавил, cascade = CascadeType.ALLвлияет на мои данные ( Userя полагаю)?

forhas
источник

Ответы:

360

Смысл в CascadeType.ALLтом, что постоянство будет распространять (каскадировать) все EntityManagerоперации ( PERSIST, REMOVE, REFRESH, MERGE, DETACH) на связанные объекты.

В вашем случае это кажется плохой идеей, так как удаление Addressприведет к удалению связанных User. Поскольку у пользователя может быть несколько адресов, другие адреса станут сиротами. Однако обратный случай (аннотирование User) имеет смысл - если адрес принадлежит только одному пользователю, безопасно распространять удаление всех адресов, принадлежащих пользователю, если этот пользователь удален.

Кстати: вы можете захотеть добавить mappedBy="addressOwner"атрибут к вашему, Userчтобы сигнализировать поставщику постоянства, что столбец соединения должен быть в таблице ADDRESS.

Kostja
источник
55
+1 за лучшее и самое короткое объяснение mappedBy, с которым я когда-либо сталкивался.
Чудесно
4
Хотя было бы неплохо иметь CascadeType.ALL на стороне @OneToMany.
mvmn
48

Смотрите здесь пример из документации OpenJPA. CascadeType.ALLозначает, что он будет делать все действия.

Quote:

CascadeType.PERSIST: при сохранении сущности также сохраняются сущности, содержащиеся в ее полях. Мы предлагаем либеральное применение этого правила каскада, потому что, если EntityManager находит поле, которое ссылается на новую сущность во время сброса, и поле не использует CascadeType.PERSIST, это ошибка.

CascadeType.REMOVE: при удалении объекта он также удаляет объекты, содержащиеся в этом поле.

CascadeType.REFRESH: при обновлении сущности также обновляйте сущности, содержащиеся в этом поле.

CascadeType.MERGE: При объединении состояния объекта также объединяются объекты, содержащиеся в этом поле.

Себастьян

seba.wagner
источник
4
Новое в JPA, эта информация полезна, но как насчет Detach здесь?
Сарц
1
В CascadeType.DETACH, при отсоединении сущности, они также отсоединяют сущности, удерживаемые родительской сущностью.
Дориан Мейер
29

Как я объяснил в этой статье и в моей книге, High-Performance Java Persistence , вы не должны использовать CascadeType.ALLна @ManyToOneтак сущность переходов состояний должна распространяться от родительских организаций к ребенку те, а не наоборот.

@ManyToOneСторона всегда Младенец ассоциация , поскольку она отображает основной столбец внешнего ключа.

Таким образом, вы должны переместить CascadeType.ALLиз @ManyToOneассоциации на @OneToManyсторону, которая должна также использовать mappedByатрибут , так как это наиболее эффективное отображение таблицы отношения один-ко-многим .

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

Из спецификации EJB3.0 :

Использование элемента каскадной аннотации может использоваться для распространения эффекта операции на связанные объекты. Каскадная функциональность чаще всего используется в родительско-дочерних отношениях.

Если X является управляемым объектом, операция удаления приводит к его удалению. Операция удаления каскадно относится к объектам, на которые ссылается X, если отношения между X и этими другими объектами аннотируются значением элемента аннотации cascade = REMOVE или cascade = ALL.

Таким образом, вкратце, отношения сущностей, определенные с помощью CascadeType.All, гарантируют, что все события постоянства, такие как persist, refresh, merge и remove, которые происходят в родительском объекте, будут переданы дочернему элементу. Определение других CascadeTypeпараметров предоставляет разработчику более детальный уровень контроля над тем, как ассоциация сущностей обрабатывает постоянство.

Например, если у меня есть объект Book, который содержит список страниц, и я добавляю объект страницы в этот список. Если @OneToManyаннотация, определяющая связь между Книгой и Страницей, помечена как CascadeType.All, сохранение Книги приведет к тому, что Страница также будет сохранена в базе данных.

Кевин Бауэрсокс
источник
11

В JPA 2.0, если вы хотите удалить адрес, если вы удалили его из сущности пользователя, вы можете добавить orphanRemoval=true(вместо CascadeType.REMOVE) в свой@OneToMany .

Больше объяснения между orphanRemoval=trueи CascadeType.REMOVEнаходится здесь .

Эмильен Бандит
источник
4

Если вы просто хотите удалить адрес, назначенный пользователю, и не влиять на класс сущности User, попробуйте что-то вроде этого:

@Entity
public class User {
   @OneToMany(mappedBy = "addressOwner", cascade = CascadeType.ALL)
   protected Set<Address> userAddresses = new HashSet<>();
}

@Entity 
public class Addresses {
   @ManyToOne(cascade = CascadeType.REFRESH) @JoinColumn(name = "user_id")
   protected User addressOwner;
}

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

szachMati
источник