4

In some cases in my experience I encountered that following usage is wrong:

entity.setElements(newCollection);

And I get error like this:

"A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instance".
So I have to rewrite it like this:

entity.getElements().clear();
entity.getElements().addAll(newCollection)

Can you please explain why does it happen?

Is it only orphan related issue?

Community
  • 1
  • 1
gstackoverflow
  • 36,709
  • 117
  • 359
  • 710

1 Answers1

4

When you perform entity.setElements(newCollection) in java, you actually replace the existing collection with a new one. What happen to elements that were in the existing collection ? They become orphan. Then the garbage collector comes into action and removes (i.e. free the memory used by) the orphan objects. All this occur in RAM, it's a thread running in the JRE, no database involved so far.

Unfortunately there is nothing like a garbage collector in relational databases. You (or in this case Hibernate) have to explicitly delete orphan records using a delete statement.

Hibernate will therefore trigger delete statement for each Collection.remove() or Collection.clear().

Also, there is no way for Hibernate to intercept the JRE garbage collector action to trigger it's own set of delete statements. So when you do entity.setElements(newCollection), Hibernate detects that previous collection has been garbage collected, but it's too late, it has no reference anymore to the oprhan objects, thus it cannot make the delete statements.

Benoit
  • 5,118
  • 2
  • 24
  • 43
  • I get the feeling that you are on to a good explanation, but not quiete. E.g. the Hibernate session tracks all instances it has fetched (that's how it can guarantees that fetching a given Entity will always return the same object instance). Therefore, these objects are not available for GC, they are held by the session. (At least this is may understanding). – GPI Oct 01 '18 at 09:02
  • I'd rather guess that Hibernate tracks the changes to the colletion ITSELF, not the elements inside of it. – GPI Oct 01 '18 at 09:07
  • @GPI "Hibernate session tracks all instances it has fetched" - maybe, but in `entity.setElements(newCollection)` the elements are not fetched if it's a lazy-loaded collection. – Benoit Oct 01 '18 at 09:15
  • @GPI well, if we look at the OP original problem, my question is how can hibernate detect this to begin with and besides using an IdentityHashMap for that List internally, I can't think of another way – Eugene Oct 01 '18 at 21:19
  • Hibernate usually instanciates specialized collections, e.g. PersistentSet instead of HashSet, and provides Lazy loading with byte code instrumentation (code you can not even "see"). So... If I were to venture a guess, I'd say Hibernate loads an entity, associates its id+class info in some data structure to trakce it, plus a link to all associations. Those associations are mapped by ad-hoc implementations that know if they have been mutated (decorate add/remove calls), so it knows what to flush. Drawback : it does not allow you to replace those collections, hence, the OP"s issue. – GPI Oct 01 '18 at 21:39