4

There is entity A referring (many-to-one) entity B, with inverse (mapped-by) reference from B to A. Also there is reference A to C and inverse reference C to A. When I issue entityManager.remove(A) then flush(), "delete" is not gerenated! But also there are no exceptions. It's just like no remove() was called at all. Why would that happen? If before remove() we extract A from reverse references B.listOfA and C.listOfA, "delete" is generated as expected.

Also note my another question where I came to conclusion that orphanRemoval not always works as expected. Now I am starting to suspect that maybe cascading worked well, but after that actual cascaded removal was "swallowed" like I described here.

Community
  • 1
  • 1
Maksim Gumerov
  • 642
  • 8
  • 23
  • Posting the entities mapping along with your remove logic would help. You may want to check whether you are starting and committing the transaction on the entity manager. – Madhusudana Reddy Sunnapu Jan 17 '16 at 17:10
  • 2
    Orphan removal should only be to entities that are privately owned. If your 'orphan' has other relationships to it, those relationships need to be nulled out - something JPA will not do for you. If any of those relationships have cascade settings on them, it may cause you issues such as this. Deleting objects that are referenced by others without fixing those references causes all sorts of issues when dealing with JPA's caching. – Chris Jan 18 '16 at 15:56

2 Answers2

11

Take a look at this answer. Basically, JPA specification mandates that a removed entity becomes managed again if the persist operation is applied to it.

To verify that this is really happening, enable the trace log level for org.hibernate package and search for log entries like:

un-scheduling entity deletion ...

To avoid any unpredictable behaviour, it is recommended that references to removed entities are removed from all the other entity instances that are loaded the same session/transaction.

Community
  • 1
  • 1
Dragan Bozanovic
  • 23,102
  • 5
  • 43
  • 110
  • 1
    Then you must be saying that A was scheduled for deletion but then JPA reconsidered because I called em.persist(A) again. But I am not calling persist() at all! The only thing I did is em.remove(A) followed by em.flush(). Or did you mean that persist(B) and persist(C) happen anyway because remove(A) touches B and C somehow (even though I did not modify them explicitly)? OK I will check that. – Maksim Gumerov Jan 18 '16 at 10:10
  • 2
    @MaksimGumerov At flush time the persist operation is cascaded to all associations from all of the entities that are present in the persistence context. – Dragan Bozanovic Jan 18 '16 at 12:24
0

I got this issue. Even though the show_sql was enabled:

<property name="hibernate.show_sql" value="true"></property>

there was no output and the remove command was silently ignored.

The issue was around an OneToMany relation where: todo 1 --- * todoitem

The environment was: - JSF - Wildfly 18 (JPA default implementation - Hibernate) - PostgreSQL 10

Attempt to delete an instance on any side fails silently, without having the respective "delete" command on the server's output and no exception message of any type.

SOLUTION

First, I switched the many side's code suggested by the Wildfly18/JBoss documentation in its examples from:

//    @Override
//    public void delete(Todoitem todoitem) {
//        if (todoitem == null) {
//            return;
//        }
//        if (!emPg.contains(todoitem)) {
//            todoitem = emPg.merge(todoitem);
//        }
//        emPg.remove(todoitem);
//        emPg.flush();
//        emPg.clear();
//    }

To an equivalent form:

@Override
public void delete(Todoitem todoitem) {
    if (todoitem == null) {
        return;
    }
    Todoitem ti = emPg.find(Todoitem.class, todoitem.getId());
    if(ti == null) {
        return;
    }
    emPg.remove(ti);
    emPg.flush();
    emPg.clear();
}

Unfortunately, I got the same result - removal silently ignored.

Then, I suspected of the database, since hibernate handles its own keys. I had done many tests during implementation... To be sure about the consistency issues, I've decided to empty the database deleting the content of both sides of the relation. Then I created new entity instances and relations using the application's resources (no SQL command) since everything was working well except for entity removal. After, I checked the database relations using SQL command to make sure everything was fine, and it was.

Next, I tried a new test again but this time it was successful.

Then I reverted the code, uncommenting the commented code and vice-versa and tested again. The test failed again, returning same result — silently ignoring remove.

I set back the code to the previous condition testing again to confirm results. The confirmations was successful, confirming suspicion of two issues, database and code implementation.

Since good news, I repeated the same to the "One" side (code below - the commented code is the initial one inherited from Wildfly 18 documentation, as above mentioned).

From:

//    @Override
//    public void delete(Todo todo) {
//        if(todo == null) {
//            return;
//        }
//        if (!emPg.contains(todo)) {
//            todo = emPg.merge(todo);
//        }
//        //todoitemDao.deleteAll(todo);
//        emPg.remove(todo);
//        emPg.flush();
//    }

To:

@Override
public void delete(Todo todo) {
    if (todo == null) {
        return;
    }
    Todo t = emPg.find(Todo.class, todo.getId());
    if(t == null) {
        return;
    }
    emPg.remove(t);
    emPg.flush();
    emPg.clear();
}

Repeated the test again, but this time no to remove a Todoitem instance ("many" side) but to remove a Todo instance ("one" side).

Issue solved. Success.

IMPORTANT NOTE:

Check the removal tests by using the SQL statements straight on the database. Sometimes the instance seems not being removed due to cache issue or code implementation fault, requiring to be treated apart.

Andre Dias
  • 41
  • 2