JPA @ManyToOne с CascadeType.ALL
Я думаю, что я неправильно понял значение каскадирования в контексте @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
, Похоже)?
5 ответов
Значение CascadeType.ALL
является то, что настойчивость будет распространять (каскад) все EntityManager
операции (PERSIST, REMOVE, REFRESH, MERGE, DETACH
) связанным лицам.
В вашем случае это кажется плохой идеей, так как удаление Address
приведет к удалению связанных User
, Поскольку у пользователя может быть несколько адресов, другие адреса станут сиротами. Однако обратный случай (аннотирование User
) имеет смысл - если адрес принадлежит только одному пользователю, безопасно распространять удаление всех адресов, принадлежащих пользователю, если этот пользователь удален.
Кстати: вы можете добавить mappedBy="addressOwner"
приписать к вашему User
сообщить поставщику постоянства, что столбец соединения должен быть в таблице ADDRESS.
Как я объяснил в этой статье и в моей книге " Высокопроизводительное постоянство Java", вы никогда не должны использовать CascadeType.ALL
на @ManyToOne
поскольку переходы между состояниями сущностей должны распространяться от родительских сущностей к дочерним.
@ManyToOne
сторона всегда является дочерней ассоциацией, поскольку она должна отображать базовый FK.
Поэтому переместите CascadeType.ALL
от @ManyToOne
ассоциация с @OneToMany
который должен использовать mappedBy
атрибут, так как это наиболее эффективное отображение один ко многим.
`
Смотрите здесь пример из документации OpenJPA. CascadeType.ALL
означает, что он будет делать все действия.
Цитата:
CascadeType.PERSIST: при сохранении сущности также сохраняются сущности, содержащиеся в этом поле. Мы предлагаем либеральное применение этого правила каскада, потому что если EntityManager находит поле, которое ссылается на новую сущность во время сброса, и поле не использует CascadeType.PERSIST, это ошибка.
CascadeType.REMOVE: При удалении сущности также удаляйте сущности, хранящиеся в этом поле.
CascadeType.REFRESH: при обновлении сущности также обновляйте сущности, содержащиеся в этом поле.
CascadeType.MERGE: При объединении состояния объекта также объединяются объекты, содержащиеся в этом поле.
Себастьян
Использование элемента каскадной аннотации может использоваться для распространения эффекта операции на связанные объекты. Каскадная функциональность чаще всего используется в родительско-дочерних отношениях.
Если X является управляемым объектом, операция удаления приводит к его удалению. Операция удаления каскадно относится к объектам, на которые ссылается X, если отношения между X и этими другими объектами аннотируются значением элемента аннотации cascade=REMOVE или cascade=ALL.
Итак, в двух словах, сущностные отношения, определенные с CascadeType.All
гарантирует, что все события персистентности, такие как persist, refresh, merge и remove, которые происходят в родительском объекте, будут переданы дочернему элементу. Определение других CascadeType
options предоставляет разработчику более детальный уровень контроля над тем, как ассоциация сущностей обрабатывает постоянство.
Например, если у меня был объект Book, содержащий список страниц, и я добавляю объект страницы в этот список. Если @OneToMany
аннотация, определяющая связь между Книгой и Страницей, помечается как CascadeType.All
сохранение книги приведет к тому, что страница также будет сохранена в базе данных.
В JPA 2.0, если вы хотите удалить адрес, если вы удалили его из объекта User, вы можете добавить orphanRemoval=true
(вместо CascadeType.REMOVE
) на ваш @OneToMany
,
Больше объяснения между orphanRemoval=true
а также CascadeType.REMOVE
здесь
Если вы просто хотите удалить адрес, назначенный пользователю, и не влиять на класс сущности "Пользователь", попробуйте что-то вроде этого:
@Entity
public class User {
@OneToMany(mappedBy = "adressOwner", cascade = CascadeType.ALL)
protected Set<Address> userAddresses = new HashSet<>();
}
@Entity
public class Adresses {
@ManyToOne(cascade = CascadeType.REFRESH) @JoinColumn(name = "user_id")
protected User adressOwner;
}
Таким образом, вам не нужно беспокоиться об использовании fetch в аннотациях. Но помните, что при удалении пользователя вы также удаляете связанный адрес с объектом пользователя.