1

I have a very simple unidirectional @OneToMany from a Parent object to a List of children with CascadeType.ALL. How would I correctly remove and delete one of the children?

Simply calling remove(child) on the List and then session.saveOrUpdate(parent) of course does not work and the child is not deleted in the database unless I specify orphan removal.

As an alternative to orphan removal, would it be correct if I session.delete(child) to delete it in the DB, then remove(child) from the List and do I then have to session.refresh(parent) so my parent object in memory has the right state?

How would I correctly remove the child and have it deleted in the database without orphan removal?

I am currently thinking about this in my 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();
    }
}

2 Answers2

3

As you area going to do the @Cascade(DELETE_ORPHAN) (from Hibernate) manually, here is the code that does pretty much the same behavior.

Entities:

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
}

And a simple example:

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();

And your Book will be deleted from the database and the lisf othe parent will be updated as well.

leozin
  • 389
  • 2
  • 7
  • Actually my main question was: How would I do it manually, without DELETE_ORPHAN? –  Oct 23 '14 at 16:44
  • I am trying to understand it better. –  Oct 24 '14 at 10:18
  • Oh ok, I will change the code to make it easier to understand. One sec – leozin Oct 24 '14 at 10:27
  • Yes, thanks! But if I don't refresh(library), then add a new book and directly call saveOrUpdate(library) again, I get a org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1 So I think I need to call refresh() before. Removing the deleted object from the collection is not enough. –  Oct 24 '14 at 11:29
  • Oh yes, if you will add another object after, so you need to refresh it. This example just shows how to remove from the collection and from the database. In fact, I'd suggest to, after you run this code, reload your object by refreshing it or using the find method again for this library ;) – leozin Oct 24 '14 at 11:47
  • OK, got it then! Thanks! Pheeeww hibernate is quite an initial uphill battle if you want to do something simple like library and books in a javafx app.... You really got to understand both the database and think SQL-like plus you got to know all of hibernate's own quirks. Like how it does not automatically delete a removed object unless you configure it. Or how you get multiple results because it uses an outer join. –  Oct 24 '14 at 12:47
  • And you either EAGER load the whole library every time or you have to play around with transactions to have an open session when you display the library. It's rather frustrating... :/ –  Oct 24 '14 at 12:54
0

Can you try below code? Removing from list before commit.

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();
    }
}
javafan
  • 1,525
  • 3
  • 21
  • 40
  • Thanks! But if I don't refresh(parent), then add a new child and directly call saveOrUpdate(parent) again, I get a org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1 So I think I need to call refresh() before. Removing the deleted object from the collection is not enough. –  Oct 24 '14 at 11:31