Вопрос о каскадировании 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)

проверьте дополнительную информацию здесь.