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: При объединении состояния объекта также объединяются объекты, содержащиеся в этом поле.

Себастьян

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

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

Если 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 в аннотациях. Но помните, что при удалении пользователя вы также удаляете связанный адрес с объектом пользователя.

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