50

I have the following code that usually works well:

public void delete(T object)
{
  EntityManager em = getPersistence().createEntityManager();
  EntityTransaction et = em.getTransaction();
  try
  {
    et.begin();
    object = em.find(object.getClass(), object.getId());
    em.remove(object);
    em.flush();
    et.commit();
  }
  catch(Exception e)
  {
    error("Unable to delete " + object.toString() + ": there are references to it.");
  }
  finally
  {
    if (et.isActive()) et.rollback();
    em.close();
  }
}

For many of my entity classes this just works. However for two of them it does nothing, it does not throw any exceptions and it does not delete the object. The log from hibernate shows that hibernate executes a number of select queries but it doesn't even try to execute a delete.

I've already tried suggestions found in other similar questions here and here, but to no avail (well, the latter suggests @Transactional which I can't use, but I just enclosed the statements between begin() and commit() instead).

I can't seem to find what those two classes have more (or less) than the others. They use @PrimaryKeyJoinColumn just like almost all other entities I have, they have @OneToMany and @ManyToOne just like ohters. To be honest, they do have a @OneToOne(optional = false) field that references another class and that other entities do not have, but I wouldn't go through the hassle of changing that (and consequently changing the database schema) unless you tell me there could be a reason for it.

Is @OneToOne responsible? Or is my delete code bugged?

Community
  • 1
  • 1
Lucio Crusca
  • 1,277
  • 3
  • 15
  • 41

6 Answers6

109

Do you have associations in this graph that cascade a persist back to the thing being deleted? If so, the JPA spec states clearly that the provider is to cancel the delete in such a case. If this is the case, Hibernate writes out a log statement saying "un-scheduling entity deletion [...]". You could see that by enabling trace logging on the org.hibernate.event.internal.DefaultPersistEventListener logger.

If this is the situation, you'll need to clean up those associations as required by JPA specification.

Ortomala Lokni
  • 56,620
  • 24
  • 188
  • 240
Steve Ebersole
  • 9,339
  • 2
  • 48
  • 46
  • that means for example that if I need CascadeType for Persist, the Owner side should be annotated? – Sergio Jan 17 '14 at 21:03
  • 5
    If you need the cascade to persist, you can leave it there. Just ensure that before deleting the entity, your code removes it from the parent list and sets the parent as null. – Rian Jan 27 '15 at 09:20
  • 7
    I have no idea how to enable logging for org.hibernate.event.internal.DefaultPersistEventListener :/ – Pieter De Bie Jul 27 '15 at 10:01
  • 17
    A bit sad that such a behavior happens silently. – Markus Barthlen Mar 06 '17 at 11:40
  • @PieterDeBie Probably, it's already late. I use logback. I added to `logback.xml` line ``. It works. – Woland Sep 15 '17 at 14:30
  • @MarkusBarthlen guess it depends if you call logging a message "silent". There is not really anything more we can do here - the spec says this has to be the behavior. – Steve Ebersole Oct 27 '17 at 14:50
  • 4
    "..JPA spec states clearly that the provider is to cancel the delete in such a case..". Can you please reference the page on which this is described? I am searching the spec with the word "cancel" and don't find anything. – Marinos An Sep 19 '18 at 08:57
  • 3.2.2 Persisting an Entity Instance – Steve Ebersole Oct 08 '20 at 11:45
  • Note: If you have an entity that `@MapsId` to another, hibernate implicitly adds a PERSIST cascade to the former. So if you try to delete the latter, what Steve mentions here will trip you up, despite you not having a CascadeType PERSIST or ALL explicitly. – Vasan Feb 24 '21 at 06:45
  • https://jakarta.ee/specifications/persistence/3.0/jakarta-persistence-spec-3.0.html#persisting-an-entity-instance is the link that @SteveEbersole pointed to. – serv-inc Nov 30 '21 at 07:22
34

Replacing the cascade = CascadeType.ALL by orphanRemoval = true in the @OneToMany association gives the expected result: The child records are correctly removed without the need to delete the parent record.

Pity that error is not logged more clearly.

jww
  • 97,681
  • 90
  • 411
  • 885
Arthur Nederlof
  • 373
  • 1
  • 4
  • 5
  • 7
    This is good if the parent is responsible for the delete; however, if the child is responsible for the delete, this can be accomplished by removing the element from the parent (destroying the reference), then deleting the child. – Thomas Mar 18 '15 at 15:30
8

I got the same issue when following things gathered together.

  1. Hibernate 3.6.10.Final
  2. Parent entity with no reference to Child.
  3. Some Child entity with reference to the Parent. It is legacy code.

    class ChildEntity {
      @ManyToOne(cascade = CascadeType.ALL)
      private ParentEntity parent;
    }
    
  4. Child entity is loaded into session context and then is removed from table without JPA notification within transaction (stored procedure, native sql)

  5. Then parent can't be deleted. No deletion, no exception, just 'un-schedule deletion' message in TRACE log.

Solution: I removed cascade attribute. It was a little bit difficult for me to find which Child entity blocks Parent deletion. Also, I do not understand why Child cascade affects if I remove Parent entity.

Andriy Slobodyanyk
  • 1,965
  • 14
  • 15
0

Besides all this, once you already have Cascade and Orphan correct, in my case I found another reason why the DELETE could not be made even when all looks fine and even when the DELETE are in the log but the database don't show any change.

It also could be a transactional issue, but, in my case it was that the Key (in Oracle Database) within the delete query was a CHAR(8) data type in the Database, in that case as you can find in the Oracle documentation CHAR versus VARCHAR2 if your data is not 8 length, the rest is cover with blank, and a JPA DELETE make distinction between "0001" and "0001 ".

I write this response due I don't see something like this in all my research and someone else could get through this post and have the same problem.

Cheers.

Eduardo
  • 517
  • 5
  • 9
  • While this answer does bring some useful stuff to the table, it does not actually answer my question. If you look at my code you see it's simply impossible to use a wrong key value, because the function loads the entity (`object` variable) beforehand: if there's no entity for the key, `object` is null and `remove()` throws an Exception as per its specification, which is not what I was experiencing. – Lucio Crusca Nov 06 '18 at 22:19
0

I had the same problem with an entity that has many relations. What I did was to set to null the parent entities, to do a merge, then make a flush and clear and finally, get the entity again using find and remove. For example:

objAgreement.setParent(null);
em.merge(objAgreement);
em.flush();
em.clear();

objAgreement= em.find(Agreement.class, objAgreement.getId())
em.remove(objAgreement);
jmoran
  • 164
  • 1
  • 5
-1

This is not answare to the question, but maybe this is what you are looking for:

@OneToMany(orphanRemoval = true,...)
  • Well, my question is nearly 10 years old now and it got the correct answer on the same day I posted it... what's the point of your non-answer? – Lucio Crusca Sep 22 '22 at 13:11