Repository navigation
Conversation
…epareEntityFlushes() that checks references to transient instances (CascadingActions.CHECK_ON_FLUSH). The Play patch only guarded the first pass with JPABase.willBeSaved, so an auto-flush could fail with a TransientObjectException on an entity that was never saved. Extract the guard into a willBeSaved(Object) helper, apply it to both passes, ship the rebuilt hibernate-core jar, and add FlushTransientReferenceTest to just-test-cases. Fixes playframework#1578
Contributor
|
Tick the box to add this pull request to the merge queue (same as
|
Contributor
Author
|
Deprecated by #1583 which is probably by far a better solution. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request Checklist
save()semantics, no behaviour is added.Helpful things
Fixes
Fixes #1578
Purpose
Since 1.11.0 (Hibernate 6.6), an entity on which
save()has not been called can still make anauto-flush fail:
This contradicts Play 1.x's contract that an entity is only written when
save()is called on it.The entity is never flushed (
HibernateInterceptor.findDirty()returns an empty array whilewillBeSavedis false), yet its associations are still validated, and the exception aborts anunrelated query. The pattern "build the object graph, then save the parts in order" is common in
Play 1.x application code and worked under 1.7, so the failure surfaces in production paths, not
only in tests.
Reproducer, with
AnEntity.parenta@ManyToOnewithout cascade:This PR extends the existing
willBeSavedguard of the Hibernate patch to the second pass ofprepareEntityFlushes(), ships the rebuilt jar, and adds a regression test.Fix
The existing reflection block is extracted into a
willBeSaved(Object)helper and applied to bothloops of
prepareEntityFlushes():The helper body is the existing code moved verbatim: same superclass walk, same swallowed
ReflectiveOperationException, and the same default oftrue, so non-Play entities and setupswithout Play on the classpath keep standard Hibernate behaviour. The first loop behaves as before
(
continueon!willBeSavedbecomes a short-circuit in the same position, beforeflushable). Thepatch's net line count is unchanged, so the later collection-interceptor hunks keep their existing
offsets.
Background Context
Root cause
In Hibernate 6.x,
AbstractFlushingEventListener.prepareEntityFlushes()iterates the persistencecontext twice:
cascadeOnFlush(...);Cascade.cascade(CascadingActions.CHECK_ON_FLUSH, CascadePoint.BEFORE_FLUSH, ...)to detectreferences to transient instances.
hibernate-6.6.31-patch-play.patchinserts theplay.db.jpa.JPABase.willBeSavedguard into loop 1only. Loop 2 is unguarded and runs for every flushable managed entity on every auto-flush.
The check hits associations without cascade specifically:
CascadeStyles.NONE.doCascade(action)returns
trueonly forCHECK_ON_FLUSH, so no-cascade associations are exactly the ones inspected(a
cascade = PERSISTassociation returnsfalseand is skipped, which is why adding it works asan application-side workaround).
Compatibility note
This is a deliberate reduction in Hibernate's checking, not only a bug fix: after this change a Play
application gets the transient-reference check during an explicit
save()(wherewillBeSavedistrue) and never on an incidental auto-flush. That is Play 1.x's documented model and matches the
1.7 / Hibernate 5 behaviour. Applications that relied on 1.11.0 to catch missing saves early will
get the exception at
save()time instead of at flush time.No API change, no configuration change, no change for non-Play users of the patched jar.
The rebuilt jar
framework/lib/hibernate-core-6.6.31.Final.patched.jaris updated in this PR, since the vendored jaris what
framework/dependencies.ymlresolves and the new test fails against the 1.11.0 jar.The binary change is deliberately minimal and auditable: only
org/hibernate/event/internal/AbstractFlushingEventListener.classdiffers from the 1.11.0 jar(entry CRC
91b5f23dto1c5c3599). It was produced by applying the patch'sAbstractFlushingEventListenersection to the pristine 6.6.31.Final source, recompiling that singlefile against the jars already in
framework/libwithjavac --release 11to match the existingbytecode level (major version 55), and replacing the one entry in the archive. No other class,
resource or manifest entry is touched.
To verify:
Because it was not produced by the Hibernate gradle build described in
framework/patches/hibernate-6.6.31-patch-play.README, it is not byte-identical to an officialrebuild. Happy to replace it with a full gradle-built artifact if you prefer that for the release.
References
CascadingActions.CHECK_ON_FLUSHis new in Hibernate 6.x; the Play patch hunk forprepareEntityFlushes()predates it.Testing done
samples-and-tests/just-test-cases/test/FlushTransientReferenceTest.java, using theexisting
AnEntity.parentmapping (a@ManyToOnewith no cascade).unsavedOwnerIsNotCheckedForTransientReferencesfails withTransientObjectExceptionfromAbstractFlushingEventListener.prepareEntityFlushes, triggeredby the auto-flush of
AnEntity.count().hibernate-core-6.6.31.Finalsources fromMaven Central.
(
AbstractFlushingEventListener.class), at the same bytecode level (major version 55). Itsbytecode shows the
willBeSavedguard ahead of bothcascadeOnFlushand theCHECK_ON_FLUSHcascade, and the existing collection-interceptor hunks still test their boolean results
(
ifeq, notpop).I put the target as master but it would be welcome to have a 1.11.1 with this change only included if possible.