Hi Jadon, Thanks for reporting this and for providing a test case.
This seems like an issue, the problem is in the old logic of callback invocation in the TransactionFilter, changes are captured at the beginning of the processing by DataChannelSyncCallbackAction and are not updated if anything new appears [1] Opened issue in JIRA to track this further [2] [1] https://github.com/apache/cayenne/blob/master/cayenne/src/main/java/org/apache/cayenne/tx/TransactionFilter.java#L42-L69 [2] https://issues.apache.org/jira/browse/CAY-2907 On Thu, Nov 6, 2025 at 1:37 AM Jadon G Hansell <[email protected]> wrote: > Hello, > > I’m running into an issue that I don’t think is intended behavior, but I’m > not sure. > > I’ll use the test entities for an example. Let’s say I register a > PreUpdate callback on Artist that updates the description of all their > paintings to read “Painted by artistName”, and I also register a PostUpdate > callback on Painting. If the painting had no changes before the commit, > then the description is updated correctly but the PostUpdate callback on > the painting is never called. Is it intended that PostUpdate callbacks are > only triggered on objects that had changes before committing? > > Here is a patch for a breaking test in 4.2 with that situation. The > painting’s description is updated correctly, but `isPostUpdated` is still > false at the end: > > ``` > Subject: [PATCH] cay-missing-post-update > --- > Index: > cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/Painting.java > IDEA additional info: > Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP > <+>UTF-8 > =================================================================== > diff --git > a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/Painting.java > b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/Painting.java > --- > a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/Painting.java > (revision c282ada74f696d181a569f15681f948ac2456914) > +++ > b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/Painting.java > (date 1762365288222) > @@ -27,11 +27,13 @@ > protected boolean postAdded; > protected boolean preRemoved; > protected boolean preUpdated; > + protected boolean postUpdated; > > public void resetCallbackFlags() { > postAdded = false; > preRemoved = false; > preUpdated = false; > + postUpdated = false; > } > > public void postAddCallback() { > @@ -46,6 +48,10 @@ > preUpdated = true; > } > > + public void postUpdateCallback() { > + postUpdated = true; > + } > + > public boolean isPostAdded() { > return postAdded; > } > @@ -58,6 +64,10 @@ > return preUpdated; > } > > + public boolean isPostUpdated() { > + return postUpdated; > + } > + > public boolean isValidateForSaveCalled() { > return validateForSaveCalled; > } > Index: > cayenne-server/src/test/java/org/apache/cayenne/access/DataDomainCallbacksIT.java > IDEA additional info: > Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP > <+>UTF-8 > =================================================================== > diff --git > a/cayenne-server/src/test/java/org/apache/cayenne/access/DataDomainCallbacksIT.java > b/cayenne-server/src/test/java/org/apache/cayenne/access/DataDomainCallbacksIT.java > --- > a/cayenne-server/src/test/java/org/apache/cayenne/access/DataDomainCallbacksIT.java > (revision c282ada74f696d181a569f15681f948ac2456914) > +++ > b/cayenne-server/src/test/java/org/apache/cayenne/access/DataDomainCallbacksIT.java > (date 1762365550630) > @@ -336,6 +336,37 @@ > assertSame(a1, listener2.getPublicCalledbackEntity()); > } > > + static class ArtistAttributionListener { > + public void updatePaintingAttributions(Artist artist) { > + String attribution = "Painted by " + artist.getArtistName(); > + for (Painting painting : artist.getPaintingArray()) { > + painting.setPaintingDescription(attribution); > + } > + } > + } > + > + @Test > + public void testPostUpdate_ChangedInPreUpdate() { > + LifecycleCallbackRegistry registry = > resolver.getCallbackRegistry(); > + > + Artist a1 = context.newObject(Artist.class); > + a1.setArtistName("XX"); > + > + Painting p1 = context.newObject(Painting.class); > + p1.setToArtist(a1); > + p1.setPaintingTitle("Painting 1"); > + > + context.commitChanges(); > + assertFalse(p1.isPostUpdated()); > + > + registry.addListener(LifecycleEvent.PRE_UPDATE, Artist.class, new > ArtistAttributionListener(), "updatePaintingAttributions"); > + registry.addCallback(LifecycleEvent.POST_UPDATE, Painting.class, > "postUpdateCallback"); > + a1.setArtistName("ZZ"); > + context.commitChanges(); > + assertEquals(p1.getPaintingDescription(), "Painted by " + > a1.getArtistName()); > + assertTrue(p1.isPostUpdated()); > + } > + > @Test > public void testPostRemove() { > ``` > > Thank you, > Jadon Hansell -- Best regards, Nikita Timofeev
