Подтвердить что ты не робот

Спящий режим, удаляющий сирот при обновлении коллекции

Я нахожу, что сиротские записи не удаляются при удалении из коллекции в 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.

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

EDIT: Спасибо за все отзывы. В дополнение к комментариям ниже, я добавил @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN) к родительскому, так что теперь:

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

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

4b9b3361

Ответ 1

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

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

В основном, чтобы сделать это автоматически, вы захотите этого в коллекции в родительском объекте:

org.hibernate.annotations.CascadeType.DELETE_ORPHAN

Ответ 2

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

@OneToMany(orphanRemoval=true)

Ответ 3

Параметр каскада в аннотации @OneToMany представляет собой массив, который вы хотите:

@OneToMany(cascade={CascadeType.ALL, CascadeType.DELETE_ORPHAN})

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

Похоже, вам не хватает аннотации mappedBy. Попробуйте:

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

Ответ 7

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