Гибернация с удалением сирот при обновлении коллекции

Я обнаружил, что сиротские записи не удаляются при удалении из коллекции в Hibernate. Я, должно быть, делаю что-то простое (это Hibernate-101!), Но не могу его найти ..

Учитывая следующее:

public class Book {
    @ManyToOne
    @NotNull
    Author author;
}
public class Author
{
    @OneToMany(cascade={CascadeType.ALL})
    List<Book> books;
}

И следующий код обновления:

Author author = authorDAO.get(1);
Book book = author.getBooks().get(0);
author.getBooks().remove(0);
authorDAO.update(author);

Фрагмент AuthorDAO:

@Override
public void update(T entity) {
    getSession().update(entity);
}

Следующий тест не проходит:

Author author = author.get(1);
assertEquals(0,author.getBooks().size()); // Passes
Book dbBook = bookDAO.get(book.getId())
assertNull(dbBook); // Fail!  dbBook still exists!
assertFalse(author.getBooks().contains(dbBook) // Passes!

Таким образом, я нахожу:

  • Несмотря на то, что книга удалена из авторской коллекции книг, она все еще существует в базе данных.
  • Если я изучу book.getAuthor().getBooks(), книги в этой коллекции нет

Это «ощущение», как будто я не сбрасываю сессию или не вызываю обновление соответствующим образом - но я не уверен, где мне это делать. В этой вене есть и другие моменты, которые могут влиять:

  • Я выполняю вышеуказанное в тесте JUnit, украшенном @RunWith(SpringJUnit4ClassRunner.class)
  • Первоначально я столкнулся с этой проблемой в подпрограмме обновления, украшенной символом @Transactional, однако с тех пор я воссоздал ее в простом старом тесте JUnit.

Любой совет будет очень признателен!

РЕДАКТИРОВАТЬ: Спасибо за все отзывы. В дополнение к комментариям ниже я добавил @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN) к родительскому элементу, так что теперь он:

public class Author
{
    @OneToMany(cascade={CascadeType.ALL})
    @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
    List<Book> books;
}

Я все еще нахожу те же результаты. Я ДОЛЖЕН упустить что-то простое.


person Marty Pitt    schedule 20.08.2009    source источник


Ответы (7)


Вы не делаете ничего плохого. Вы просто не удаляете дочернюю сущность. Вы можете сделать это либо с помощью явного удаления () дочерней сущности (в дополнение к тому, что вы делаете), либо использовать эту аннотацию, которая вызывает удаление потерянных записей.

Также стоит отметить, что CascadeType.DELETE тоже не удаляет сирот. Это означает другое. См. JPA CascadeType.ALL не удаляет сирот.

В основном, чтобы сделать это автоматически, вам понадобится это в коллекции в родительском элементе:

org.hibernate.annotations.CascadeType.DELETE_ORPHAN
person cletus    schedule 20.08.2009
comment
Я добавил эту аннотацию, но она все еще не работает - те же результаты, что и раньше. - person Marty Pitt; 20.08.2009
comment
Вы также устанавливаете ссылку на автора в книге на null? - person cletus; 20.08.2009
comment
Нет, я думал, что в этом суть аннотации DELETE_OPRHAN? - person Marty Pitt; 20.08.2009
comment
Вам все равно придется самостоятельно управлять отношениями Java. Это означает, что вы должны установить для родителя значение null в дочернем элементе, если связь двунаправленная. - person cletus; 20.08.2009

для людей, которые ищут свое решение: теперь в Hibernate, соответственно. JPA 2.0, это правильный путь:

@OneToMany(orphanRemoval=true)
person Jakub    schedule 12.12.2012

Параметр cascade в аннотации @OneToMany - это массив, вам нужно:

@OneToMany(cascade={CascadeType.ALL, CascadeType.DELETE_ORPHAN})
person Sindri Traustason    schedule 20.08.2009
comment
не будет ли CascadeType.ALL также охватывать CascadeType.DELETE_ORPHAN? - person Jess; 16.06.2018
comment
Этому ответу сейчас 9 лет. Наверное, это уже не актуально. - person Sindri Traustason; 18.06.2018

Попробуйте использовать следующую аннотацию, если вам нужно поведение «транзитивной зависимости».

@ org.hibernate.annotations.Cascade (CascadeType.DELETE_ORPHAN)

person JARC    schedule 20.08.2009

пожалуйста, добавьте @onDelete, возможно, это сработает для вас

public class Author
{
    @OneToMany(cascade={CascadeType.ALL})
    @OnDelete(action = OnDeleteAction.CASCADE)
    @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
    List<Book> books;
}
person Am1rr3zA    schedule 20.08.2009
comment
Привет. Это тоже не имеет никакого эффекта. - person Marty Pitt; 20.08.2009
comment
вы фиксируете сеанс или сбрасываете его после удаления? - person Am1rr3zA; 20.08.2009

Похоже, вам не хватает аннотации mappedBy. Пытаться:

public class Book {
  @ManyToOne
  @NotNull
  Author author;
}
public class Author {
  @OneToMany(mappedBy="author", cascade={CascadeType.ALL})
  List<Book> books;
}
person Civil Disobedient    schedule 21.08.2009

Я знаю, что опаздываю на вечеринку, и я понимаю, что уже много было сказано по этой теме, но ИМХО я бы не стал использовать delete-orphans в качестве каскадного варианта. Я бы предпочел удалить дочерние ассоциации вручную, прежде чем удалять родительские. Одна небольшая оплошность ... и длинная цепочка ассоциаций может быть удалена за секунду. Да! Я согласен, что это может быть именно то, что может понадобиться при некоторых обстоятельствах.

person Sanjeev Dhiman    schedule 01.07.2019