Issue with Insertion Order For Reflexive DataObjects

Matt R Watson <[email protected]> Mon, 6 Feb 2023 10:40:32 -0800
Newsgroups gmane.comp.java.cayenne.user
Message-ID <[email protected]>
Following the instructions on: Cayenne How can I help? <https://cayenne.apache.org/how-can-i-help.html>

I have created a JIRA issue: https://issues.apache.org/jira/browse/CAY-2792

And a pull request that includes the breaking test (without a fix yet): https://github.com/apache/cayenne/pull/563


Has anyone else encountered this issue? Seems like it would be a common enough structure, but maybe I am mistaken.

Here is the text from the JIRA issue:
> For terminology's sake, I assume Reflexive refers to when you have 2 objects of the same Model, that are related, in this case, parent/child (ToOne/HasMany). If this assumption is incorrect we can rename the title.
> 
> For a little history on this, we never encountered this bug until we upgraded to 4.2.RC1, and I recently tested on 4.2.RC2and the bug still exists.
> 
> For a while I thought this was only a bug in our environment, thinking that our code was causing the issue because Cayenne has a passing test in CayenneDataObjectRelationshipsIT.testReflexiveRelationshipInsertOrder1. But as I got to digging, the error occurs when the parent also has a relationship to some "Other" object that is not of the same model.
> 
> My commit has a breaking test. I have not worked on a fix yet, but will begin investigating. I suspect it has to do with the work done in CAY-2571 <https://issues.apache.org/jira/browse/CAY-2571>, but I am not certain yet.
> 
> The issue is that Cayenne randomly tries to commit the child record in front of the parent record, therefore the database throws a Foreign Key error, since the parent does not yet exist when the child is inserted. Because of this randomness, my breaking test attempts this process 100 times. You will also see another test that passes with the same Models but without setting the "ToOne" Other object.
> 
> here is a look at the code that "randomly" fails:
> 
>     public void addReflexiveParentAndChildWithOtherRelationshipOnParent() {
>         // can add Reflexive Parent (that belongsTo Other) and Child,
>         // we will do this 100 times, because it randomly does it correctly/incorrectly
> 
>         // given some "other" Object
>         final Other other = context.newObject(Other.class);
>         other.setName("OtherB");
>         context.commitChanges();
> 
>         final int attempts = 100;
>         int errors = 0;
> 
>         for (int i = 0; i < attempts; i++) {
>             // when parent is created and associated to "Other"
> 
>             final Reflexive parent = context.newObject(Reflexive.class);
>             parent.setName("parentB"+i);
>             parent.setToOther(other);
> 
>             // and child is created and associated to "Parent"
>             final Reflexive child = context.newObject(Reflexive.class);
>             child.setName("childB"+i);
>             child.setToParent(parent);
> 
>             try {
>                 context.commitChanges();
>             } catch (final Exception e) {
>                 errors++;
>                 e.printStackTrace();
>                 context.rollbackChanges();
>             }
>         }
> 
>         // then no error occurred
>         assertEquals(String.format("Failed on %s of %s attempts.", errors, attempts), 0, errors);
>     }
>  
> 

If you would like me to elaborate more on how we use this structure in in the real world application I can.

Thanks for any help offered out there, as I begin digging on a solution.

Cheers,
Matt Watson
[email protected] <mailto:[email protected]>