Hi Chris!
I revisited the whole entity model.
'First' entity is referenced only by:
- FirstCollection: since I remove the first entity from the
first.getFirstCollection().getFirsts() collection before
deleting 'first', this relationship shouldn't be causing any
issue
- FirstCollection.thirds which is a @OneToMany relationship
towards a Third entity which itself contains a @ManyToMany
relationship with First
Substantially, First has:
@ManyToMany(
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "firsts")
private Collection<Third> thirds;
and Third has:
@ManyToMany(
cascade = { CascadeType.DETACH, CascadeType.PERSIST,
CascadeType.REFRESH },
fetch = FetchType.EAGER)
@OrderBy("id ASC")
private List<First> firsts;
While, concentrating on FirstCollection, this has:
@OneToMany(
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "firstCollection",
orphanRemoval = true)
private Collection<Third> thirds;
and Third has:
@ManyToOne(
cascade = { CascadeType.DETACH, CascadeType.PERSIST,
CascadeType.REFRESH },
fetch = FetchType.EAGER,
optional = false)
private FirstCollection firstCollection;
So: the FirstCollection may be actually resurrecting the First
instance if its 'thirds' collection is lazily loaded and that
collection contains a Third instance that in turn references
'first'.
However, I'm not reading nor updating any Third instance in my
piece of code (neither before the removes, nor after), so I don't
even think I should trigger the loading of FirstCollection.thirds,
not to say about refreshing.
Anyway, one thing I'm doing wrong is that I should take care of
updating the in-memory model in order to remove all Thirds
instances from FirstCollection.thirds which reference the
soon-to-be-deleted 'first' instance.
However, if we forget this problem for a moment, since the
'thirds' field in First is annotated with "CascadeType.ALL", I
would expect the deletion to be cascaded (at least at the database
level) to rows corresponding to such Third entities.
In fact, if I look at the SQL generated by EclipseLink when the
deletion is successful, I actually see that EclipseLink is doing
the right thing:
DELETE FROM `Third_First` WHERE (`first_id` = ?)
DELETE FROM `First` WHERE (`first_id` = ?)
(a lot of DELETE FROM entities related to Second)
DELETE FROM `Second` WHERE ((`id` = ?) AND (`version` = ?))
Instead, when the deletion fails, I do not see the first two
DELETEs.
From this, I would even say that the deletion goes WELL when the
object graph is certainly complex, and goes BAD when it seems like
(but I can't be sure) to be less complex.
However I'm sure it goes well even when the graph is simple. The
main problem is that I still could not reproduce the bad behaviour
in my test environment: I tried various combinations of data and
application paths without success. I only see the problem
happening (sometimes) in the production environment... and I could
not yet identify the necessary steps.
So, now I'm going to add the necessary code to cleanup
firstCollection.getThirds() from Third instances referencing the
'first' instance and see if this will fix the problem. If this
goes well, the lesson learnt is "always ALWAYS make sure the
in-memory model is FULLY correct before deleting entities"...
otherwise hard-to-explain things may happen in certain
circumstances.
Thanks again for your help!
Mauro
Il 02/03/2016 15:59, christopher delahunt ha scritto:
Hello Mauro,
I mistyped when I wrote cascade remove - I meant to write cascade
merge or cascade persist. Any references to 'first' could be
causing it to be resurrected, not just ones from the 'second'
instance's object graph. I also noticed you are using cascade
refresh and lazy relationships - refresh of the referenced entity
over a lazy reference will be delayed until it is accessed,
potentially wiping out changes made so far. Something that causes
a refresh of firstCollection for instance might get triggered
after the remove call but before the flush, and cause
firstCollection to still reference first and cause its
resurrection in the persistence unit. I would detail all the
references in your object model to the 'first' class and check
that one isn't interfering.
If you haven't already, I'd recommend turning on EclipseLink
logging and matching statements up to the calls in your code
causing them, and trying to determine what is different when the
issue occurs vs when it doesn't. You might also try delaying or
removing the constraint, so that you can see if EclipseLink
eventually issues the delete so we can see if it is just an
ordering issue or if it never does, and so is a resurrection issue
in the application references as mentioned before.
Best of luck,
Chris
You should turn on logging to see the statements executed in
relation to your calls, and possibly relax the
On 02/03/2016 4:06 AM, Mauro Molinari
wrote:
Hi Chris,
thank you for your help.
Actually, all the mentioned code is executed within the same
transactional method body. No data change is performed before.
firstCollection
is obtained directly from first.getFirstCollection()
.
Between this call and the removal of first there are only
other getters calls (which return other related entities that
are not modified):
first.getFoo()
firstCollection.getBar()
first.getSomethingElse()
Then, after the deletion of second
, and before
the query, there's nothing.
The First
entity is certainly not referenced by
Second
, nor by any remove-cascaded entity related
to Second
(it was an explicit design choice).
In the actual code I'm using Spring Data JPA to perform
deletions and queries, but I don't think it should matter.
What I may try to do is to clear the reference between first
and second before applying deletions, something like:
Second second = first.getSecond()
;
first.setSecond(null);
entityManager.remove(first);
entityManager.remove(second);
but it's a shot in the dark. Also, I'm not sure what the
"lesson learnt" should be, in order to avoid this problem in
the future...
Thanks again,
Mauro
Il 01/03/2016 19:48, christopher delahunt ha scritto:
Hello Mauro,
Is there anything else that might reference the first
instance? Could the
firstCollection.
getFirsts().remove(first)
change be being done on an unmanaged instance so that the
FirstCollection instance still references the first instance
in the context?
This sometimes happens if you have something left in your
object model still referencing the first instance with a
relationship marked cascade remove or cascade persist, causing
the traversal of the object model to 'undo' the remove call.
This may be inconsistent due to lazily fetched relationships,
and how the traversal of the model discovers unmanaged
instances.
Best Regards,
Chris
On 01/03/2016 12:08 PM, Mauro
Molinari wrote:
Hello,
I have the following problem that puzzles me.
I have an entity, let's call it First, which references
another entity, let's call it Second.
The relationship is this:
@Entity
public class First {
// ...
@OneToOne(
cascade = { CascadeType.DETACH,
CascadeType.PERSIST,
CascadeType.REFRESH },
fetch = FetchType.LAZY,
optional = false,
orphanRemoval = false)
private Second second;
}
The relationship is unidirectional, i.e. there's no
@OneToOne relationship mapped in Second towards First.
First also has a @ManyToOne relationship towards a
FirstCollection entity. I don't think it should matter at
all, but I say it for completeness.
Then I have some code that takes a First instance (let's
call it "first") and does some things like:
FirstCollection firstCollection =
first.getFirstCollection();
// do some read-only operations on first
// remove first from the FirstCollection
related entities
firstCollection.getFirsts().remove(first);
// delete first
entityManager.remove(first);
// delete the related second instance
entityManager.remove(first.getSecond());
// perform a read query on an entity related to
first
When the last step is executed, a flush before the execution
of the read query is performed; I see something like this in
the stack trace:
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:879)
at org.eclipse.persistence.internal.jpa.QueryImpl.performPreQueryFlush(QueryImpl.java:967)
at org.eclipse.persistence.internal.jpa.QueryImpl.executeReadQuery(QueryImpl.java:207)
The problem is that this lfush seems to perform the deletion
of second (together with some of its cascaded deleted
entities) BEFORE the deletion of First. I mean, the log
shows a call to DELETE on the Second table, but no previous
delete on the First table. This causes a foreign key
constraint failure.
This seems to happen only sometimes. My question is:
- how is it possible? I'm calling remove on First before
calling remove on Second!
- how to avoid this problem?
Thanks in advance,
Mauro
_______________________________________________
eclipselink-users mailing list
eclipselink-users@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://dev.eclipse.org/mailman/listinfo/eclipselink-users
_______________________________________________
eclipselink-users mailing list
eclipselink-users@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://dev.eclipse.org/mailman/listinfo/eclipselink-users
_______________________________________________
eclipselink-users mailing list
eclipselink-users@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://dev.eclipse.org/mailman/listinfo/eclipselink-users
_______________________________________________
eclipselink-users mailing list
eclipselink-users@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://dev.eclipse.org/mailman/listinfo/eclipselink-users