Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate deleting orphans when updating collection

I'm finding that orphan records aren't being deleted when removing from a collection in Hibernate. I must be doing something simple wrong, (this is Hibernate-101!), but I can't find it..

Given the following:

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

And the following update code:

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

AuthorDAO snippet:

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

The following test the fails:

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!

In summary, I'm finding:

  • While book is removed from the Author's collection of books, it still exists in the database
  • If I examine book.getAuthor().getBooks(), book does not exist in that collection

This "feels" like I'm not flushing the session or forcing an update appropriately - but I'm not sure where I should be doing that. Along that vein, other points that may be impacting:

  • I'm performing the above in a JUnit test decorated with @RunWith(SpringJUnit4ClassRunner.class)
  • I originally hit this problem inside an update routine which is decorated with @Transactional, however, I have since recreated it in a plain old JUnit test.

Any advice would be greatly appreciated!

EDIT: Thanks for all the feedback already. Further to comments below, I've added the @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN) to the parent, so it's now:

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

I'm still finding the same results. I MUST be missing something simple.

like image 351
Marty Pitt Avatar asked Aug 20 '09 08:08

Marty Pitt


3 Answers

for people searching for their solution: Now in Hibernate, resp. JPA 2.0, this is the right way:

@OneToMany(orphanRemoval=true)
like image 106
Jakub Avatar answered Nov 02 '22 23:11

Jakub


You're not doing anything wrong. You're just not removing the child entity. You can either do this with an explicit remove() of the child entity (in addition to what you're doing) or use that annotation that causes orphan records to be deleted.

Also, it's worth mentioning that CascadeType.DELETE won't delete orphans either. That means something else. See JPA CascadeType.ALL does not delete orphans.

Basically to do this automatically you'll want this on the collection in the parent:

org.hibernate.annotations.CascadeType.DELETE_ORPHAN
like image 41
cletus Avatar answered Nov 03 '22 01:11

cletus


The cascade option in the @OneToMany annotation is a array, what you want is:

@OneToMany(cascade={CascadeType.ALL, CascadeType.DELETE_ORPHAN})
like image 44
Sindri Traustason Avatar answered Nov 02 '22 23:11

Sindri Traustason