Спящий режим: удаление элемента из списка не сохраняется
У меня возникают проблемы при удалении элемента из списка. Список определен в суперклассе, но аннотации Hibernate применяются к аксессуарам свойств в подклассе. В суперклассе есть два метода, которые манипулируют списком. Метод "добавить" отлично работает, но "удалить" не сохраняется. Я проверил настройки Cascade, и у меня, похоже, все правильно. Я делаю то, что невозможно. Если нет, я делаю что-то неправильно?
Вот мои классы:
@Entity
abstract class Temporal<T> {
@Id
@GeneratedValue
private Long id;
@Version
private Integer version = null;
@Transient
protected List<T> content = new ArrayList<T>();
public void remove(T value) {
// business logic ...
content.remove(value);
}
public void add(T value) {
// business logic ...
content.add(value);
}
}
@Entity
@AccessType("property")
class TemporalAsset extends Temporal<Asset> {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "temporal")
public List<Asset> getContent() {
return super.content;
}
protected void setContent(List<Asset> list) {
super.content = list;
}
}
Я использую экземпляр класса TemporalAsset следующим образом (обратите внимание, что я использую только метод "refresh" для демонстрации поведения. Список не сохраняется корректно, даже если я скрываю или закрываю сеанс и открываю новый сеанс):
temporalAsset.add(value1);
temporalAsset.getContent().size() == 1; // true
session.update(temporalAsset);
session.refresh(temporalAsset);
temporalAsset.getContent().size() == 1; // true
temporalAsset.remove(value1);
temporalAsset.getContent().size() == 0; // true
session.update(temporalAsset);
session.refresh(temporalAsset);
temporalAsset.getContent().size() == 0; // false, its 1
Спасибо.
Ответы
Ответ 1
Вы должны явно указать каскад как CascadeType.DELETE_ORPHAN.
Попробуйте изменить код на
@OneToMany
@Cascade(cascade = {CascadeType.ALL, CascadeType.DELETE_ORPHAN}, mappedBy = "temporal")
Часть из hibernate docs:
Если срок жизни дочернего объекта равен ограниченный продолжительностью жизни родителя объекта, сделайте родителя полным объект жизненного цикла, указав CascadeType.ALL и org.hibernate.annotations.CascadeType.DELETE_ORPHAN (см. Hibernate справочное руководство по семантике сирота удалить)
Ответ 2
Попробуйте удалить вызовы на Session.refresh(). Из документов:
Перечитайте состояние данного экземпляр из базовой базы данных. Нецелесообразно использовать это для осуществлять длительные сеансы, которые охватывают многие бизнес-задачи. Этот метод однако, полезно в некоторых специальных обстоятельства. Например
- когда триггер базы данных изменяет состояние объекта при вставке или обновлении
- после выполнения прямого SQL (например, массового обновления) в том же сеансе
- после вставки Blob или Clob
http://www.hibernate.org/hib_docs/v3/api/org/hibernate/Session.html#refresh(java.lang.Object)
Если вы вызвали flush() перед обновлением(), это также может устранить проблему, поскольку flush() гарантирует, что любой ожидающий SQL будет выполняться в отношении DB. На практике я почти никогда не видел, чтобы кто-то использовал refresh(), и это не похоже на ваш код, который вам нужен.
В этой главе из документации стоит прочитать:
http://www.hibernate.org/hib_docs/v3/reference/en/html/objectstate.html
Ответ 3
Вы отметили поле "content" как переходное в суперклассе. Я бы хотя бы подозревал, что это вызывает проблемы. При сопоставлении в подклассе у вас в основном есть два противоречащих друг другу сопоставления для одного и того же атрибута.