更新集合时,Hibernate如何删除孤儿记录?

34

我发现在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(),则该书不会存在于该集合中。
这种感觉就像我没有适当地刷新会话或强制更新——但是我不确定我应该在哪里做到这一点。其他可能影响的方面如下:
- 我在装饰有@RunWith(SpringJUnit4ClassRunner.class)的JUnit测试中执行了以上操作。 - 最初我在一个带有@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;
}

我仍然得到相同的结果。我一定是漏了什么简单的东西。

7个回答

46

对于正在寻找解决方案的人:现在在Hibernate,或者JPA 2.0中,这是正确的方法:

@OneToMany(orphanRemoval=true)

20

你没有做错任何事情。你只是没有删除子实体。你可以使用显式的remove()方法删除子实体(除了你已经做的之外),或者使用那个注释来导致孤立记录被删除。

此外,值得一提的是 CascadeType.DELETE 也不会删除孤立记录。这意味着还有其他操作。请参阅JPA CascadeType.ALL does not delete orphans.

基本上,要自动执行此操作,您需要在父级中的集合上使用以下内容:

org.hibernate.annotations.CascadeType.DELETE_ORPHAN

1
我已经添加了这个注解,但它仍然不起作用 - 与之前相同的结果。 - Marty Pitt
2
你也将书中的作者引用设置为null了吗? - cletus
不,我认为这就是DELETE_ORPHAN注释的意义所在吧? - Marty Pitt
8
你仍然需要自己管理Java中的关系。这意味着,如果关系是双向的,你需要在子对象中将父对象设置为null。 - cletus

3
< p > @OneToMany注释中的级联选项是一个数组,您需要的是:

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

CascadeType.ALL会不会也包括CascadeType.DELETE_ORPHAN呢? - Jess
这个答案已经有9年的历史了,现在可能不再相关。 - Sindri Traustason

0

请添加@onDelete,也许这对您有帮助

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

嗨。这也没有任何影响。 - Marty Pitt
你在删除后提交会话还是刷新它? - Am1rr3zA

0

看起来你可能缺少了mappedBy注解。尝试一下:

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

0

我知道我来晚了,也意识到关于这个话题已经有很多讨论了,但在我看来,我不会使用delete-orphans作为级联选项。我宁愿在删除父对象之前手动删除子关联。一个轻微的疏忽...就可以在一秒钟内删除一长串关联。是的!我同意,在某些情况下,这可能正是人们想要的。


0

如果您想要“传递依赖”行为,请尝试使用以下注释。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接