Спящий режим: удаление элемента из списка не сохраняется

У меня возникают проблемы при удалении элемента из списка. Список определен в суперклассе, но аннотации 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" как переходное в суперклассе. Я бы хотя бы подозревал, что это вызывает проблемы. При сопоставлении в подклассе у вас в основном есть два противоречащих друг другу сопоставления для одного и того же атрибута.