Вопрос о каскадировании JPA
У меня есть два объекта, называемых User и UserProfile, в моем datamodel. Вот как они отображаются.
Код пользователя:
@OneToOne(cascade=CascadeType.ALL)
@PrimaryKeyJoinColumn
public UserProfile getUserProfile(){
return this.userProfile;
}
public void setUserProfile(UserProfile userProfile){
this.userProfile=userProfile;
}
Код из объекта UserProfile:
@OneToOne(mappedBy="userProfile",cascade=CascadeType.ALL)
public User getUser(){
return this.user;
}
public void setUser(User user){
this.user=user;
}
Как вы видите, у меня есть cascadetype.all для атрибута пользователя в UserProfile. Но когда я пытаюсь удалить объект UserProfile, соответствующий пользовательский объект все еще остается. (Когда я пытаюсь удалить объект User, соответствующий объект UserProfile удаляется.)
Вот мой вопрос: -
- Каскады сохраняются только тогда, когда я указываю их на объекте, обладающем связью?
Ответы
Ответ 1
Ваш вопрос не прав сам по себе, из-за чего возникает вся путаница. Артур неплохо справился с его ответом, но из комментариев видно, что путаница все еще осталась, поэтому позвольте мне нанести удар по ней здесь.
Каскады сохраняются только при указании их на объекте, владеющем отношения?
"каскад" - это атрибут, который вы указываете на одном (или, возможно, в обоих случаях в двунаправленном) конце отношения. Он определяет, какие действия выполняются на , конец будет распространен на конец другого. Есть много разных типов этих действий, определенных в JPA и еще больше определяется в Hibernate-расширениях. Это различие важно - вы должны говорить только о специфическом распространении поведения, а не о "каскаде" вообще.
PERSIST, MERGE, REFRESH распространяются нормально (с конца они были объявлены другим).
УДАЛИТЬ, однако, сложно, потому что это может означать две разные вещи. Если у вас есть отношения между A и B, и вы пытаетесь удалить A, вы можете либо удалить B на другом конце ИЛИ вы можете удалить связь, но оставить B неповрежденным.
Hibernate делает четкое различие между двумя - вы можете объявлять REMOVE (DELETE) и DELETE_ORPHAN
типы каскадов отдельно; Спецификация JPA - нет. Обратите внимание, что DELETE_ORPHAN
не поддерживается однозначными отношениями (OneToOne/ManyToOne).
Таким образом, распространение REMOVE (само по себе или когда оно является частью ALL) зависит от того, имеет ли отношение четкий владелец (однонаправленный всегда делает, двунаправленный, если он сопоставлен с использованием mappedBy и если он не сопоставлен с помощью таблицы соединений), и в этом случае он распространялся от владельца к собственному ИЛИ без владельца, и в этом случае он распространялся в любом направлении, но без семантики DELETE_ORPHAN
, если он не был явно определен. Типичным примером последнего является двунаправленное много-много.
Ответ 2
Как сказано
Когда я пытаюсь удалить объект UserProfile, соответствующий пользовательский объект все еще остается
Возможно, когда вы пытаетесь удалить UserProfile, вы получаете нарушение ограничения целостности из базы данных - используете ли вы механизм MyISAM в MySQL?
Но поскольку вы ничего не говорите об этом. Возможно, ваш объект UserProfile не имеет ссылки на объект пользователя.
Как указано в спецификации JPA
удалить операцию каскадируется для объектов, на которые ссылается X, если отношение из X к этим другим объектам аннотируется с помощью элемента cascade = REMOVE или cascade = ALL элемента аннотации
Что-то вроде
UserProfile up = entityManager.find(UserProfile.class, id);
entityManager.close();
// Notice User is null outside a persistence context
// So user will be not removed from the database because UserProfile does not have a reference to it
up.setUser(null);
entityManager.getTransaction().begin();
entityManager.remove(up);
entityManager.getTransaction().commit();
Или у вас есть что-то вроде
entityManager.getTransaction().begin();
UserProfile up = entityManager.find(UserProfile.class, id);
// throws UPDATE USER_PROFILE SET USER_ID = NULL
up.setUser(null);
// up.getUser() is null
// So user is not removed
entityManager.remove(up);
entityManager.getTransaction().commit();
В ответ на комментарий ChhsPly:
В Java Persistence with Hibernate вы увидите следующее
Каскадный атрибут направлен: Он применяется только к одному концу ассоциации.
Я думаю, что было бы лучше, чем
Он применяется только к одному концу ассоциации за операцию
Таким образом, вы можете поместить атрибут каскада в обе стороны одновременно, даже в двунаправленных отношениях. Итак, ChssPly прав.
атрибут mappdeBy устанавливает двунаправленное отношение. атрибут mappedBy обозначил объект Address как обратную сторону отношения. Это означает, что объект Customer является стороной-владельцем отношения.
ChssPly прав, когда он говорит, что mappedBy не имеет ничего общего с каскадом
Ответ 3
Правильно, когда у вас есть двунаправленные отношения, владелец диктует каскадные правила, так как это "владелец". "Собственная" сущность по существу следует порядкам, она не может давать приказы - так сказать.
Ответ 4
С JPA 2.x, если вы хотите удалить каскад, используйте атрибут orphanRemoval
:
@OneToMany(orphanRemoval=true)
проверьте дополнительную информацию здесь.