Hibernate如何正确删除@OneToMany中的孩子?

Posted

技术标签:

【中文标题】Hibernate如何正确删除@OneToMany中的孩子?【英文标题】:Hibernate How to correctly delete children in @OneToMany? 【发布时间】:2014-10-23 15:59:20 【问题描述】:

我有一个非常简单的单向@OneToMany,从父对象到 CascadeType.ALL 的子对象列表。我将如何正确删除和删除其中一个孩子?

只需在 List 上调用 remove(child) 然后 session.saveOrUpdate(parent) 当然不起作用,并且除非我指定孤儿删除,否则不会在数据库中删除子项。

作为孤立删除的替代方法,如果我 session.delete(child) 在数据库中删除它,然后从列表中删除(child) 并且我必须 session.refresh(parent) 所以是否正确我在内存中的父对象有正确的状态?

我如何正确删除子项并将其从数据库中删除而不进行孤立删除?

我目前正在 ParentDao 中考虑这个问题:

public void removeChild(Parent parent, Child child) 
    Session session = HibernateUtil.getSessionFactory().openSession();
    Transaction tx = null;
    try 
        session.beginTransaction();

        session.delete(child);

        session.getTransaction().commit();

        parent.getChildren().remove(child);
        session.refresh(parent);
     catch (RuntimeException e) 
        if (tx != null) 
            tx.rollback();
        
        throw e;
     finally 
        session.close();
    

【问题讨论】:

【参考方案1】:

当您打算手动执行 @Cascade(DELETE_ORPHAN)(来自 Hibernate)时,这里是执行几乎相同行为的代码。

实体:

class Library 

  @Id
  private Integer id;

  @OneToMany
  private List<Book> books;

  // getters and setters    


class Book 

  @Id
  private Integer id; 

  @ManyToOne
  private Libraby library;

  // getters and setters

还有一个简单的例子:

session.beginTransaction();

// load a library with ID = 1
Library library = session.get(Library.class, 1);

// assuming that your library has two books
Book book = library.getBooks().get(0); //gets the first book
library.getBooks().remove(0); // Remove this book from the library
session.delete(book);

session.getTransaction().commit();

您的 Book 将从数据库中删除,并且父级的 lisf 也将更新。

【讨论】:

实际上我的主要问题是:如果没有 DELETE_ORPHAN,我将如何手动完成? 我正在努力更好地理解它。 哦,好的,我将更改代码以使其更易于理解。一秒 好的,谢谢!但是如果我不刷新(图书馆),然后添加一本新书并再次直接调用 saveOrUpdate(图书馆),我得到一个 org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0];实际行数:0;预期:1 所以我想我需要先调用 refresh() 。从集合中移除已删除的对象是不够的。 哦,是的,如果你之后要添加另一个对象,那么你需要刷新它。此示例仅显示如何从集合和数据库中删除。事实上,我建议,在你运行这段代码之后,通过刷新它或再次使用这个库的 find 方法来重新加载你的对象;)【参考方案2】:

你能试试下面的代码吗?在提交之前从列表中删除。

public void removeChild(Parent parent, Child child) 
    Session session = HibernateUtil.getSessionFactory().openSession();
    Transaction tx = null;
    try 
        session.beginTransaction();

        parent.getChildren().remove(child); 
        session.delete(child);

        session.getTransaction().commit();


       // session.refresh(parent); // this is not required
     catch (RuntimeException e) 
        if (tx != null) 
            tx.rollback();
        
        throw e;
     finally 
        session.close();
    

【讨论】:

谢谢!但是如果我不刷新(父),然后添加一个新的子并再次直接调用 saveOrUpdate(parent),我得到一个 org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0];实际行数:0;预期:1 所以我想我需要先调用 refresh() 。从集合中移除已删除的对象是不够的。

以上是关于Hibernate如何正确删除@OneToMany中的孩子?的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate 无法在多个包上正确操作

Hibernate/H2 @OneToMany 删除子时“违反参照完整性约束”?

JPA 2 / Hibernate 孤儿删除仍然无法与@OneToMany 一起使用?

JPA 2.0 (Hibernate) 使用 @JoinTable 为 @OneToMany 生成不正确的连接表 PK

Hibernate @OneToMany 在更新父级时从列表中删除子级

删除元素时使用 JoinTable 和 OrderColumn 的 Hibernate 单向 OneToMany 映射中的约束冲突