Skip to content

HHH-20515 Fix findMultiple overwriting session state with cache values#12880

Open
jjennnn wants to merge 1 commit into
hibernate:mainfrom
jjennnn:HHH-20515
Open

HHH-20515 Fix findMultiple overwriting session state with cache values#12880
jjennnn wants to merge 1 commit into
hibernate:mainfrom
jjennnn:HHH-20515

Conversation

@jjennnn

@jjennnn jjennnn commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Added check in CacheLoadHelper.convertCacheEntryToEntity() to return managed entities without reassembling from cache, preserving uncommitted changes. Added FindMultipleCacheTest that verifies findMultiple() keeps in-memory entity changes when called multiple times within the same session.


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license
and can be relicensed under the terms of the LGPL v2.1 license in the future at the maintainers' discretion.
For more information on licensing, please check here.



Please make sure that the following tasks are completed:
Tasks specific to HHH-20515 (Bug):

  • Add test reproducing the bug
  • Add entries as relevant to migration-guide.adoc OR check there are no breaking changes

https://hibernate.atlassian.net/browse/HHH-20515

@mbellade mbellade left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jjennnn thanks for working on this, but I'm not sure this is the correct approach: changing the common 2nd level cache load path (which is used across many different methods, not only findMultiple()) to always return the persistent instance if one is found in the current session may be ok for ``findMultiple( ..., SessionCheckMode.ENABLED ), but it would conflict with the DISABLED` semantic. For the latter, we should always avoid looking in the PC and just return a (transient?) instance initialized with the values contained in the cache without refreshing its state.

Looking at the findMultiple() contract though, it says it returns persistent instances, and I'm not sure how we can avoid "refreshing" the state of an existing entity if we're supposed to skip checking the PC for it. @sebersole any insights on the intended behavior?

@mbellade mbellade left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jjennnn your approach is still doing persistence context lookups upfront, even if the SessionCheckMode would be set to DISABLED. We need to avoid such lookups, and just return any existing persistent entity instance after the 2nd level cache lookup or the query occurred.

Edit: please add tests for both SessionCheckMode.DISABLED and ENABLED (passing it explicitly so we don't depend on the current default), as well as CacheMode.REFRESH_SESSION since your changes seem to account for that as well.

@jjennnn
jjennnn force-pushed the HHH-20515 branch 5 times, most recently from eb2819c to 61a2e0f Compare July 7, 2026 14:34
Comment thread hibernate-core/src/main/java/org/hibernate/loader/internal/CacheLoadHelper.java Outdated
@jjennnn
jjennnn force-pushed the HHH-20515 branch 2 times, most recently from 2b1973f to 0902c64 Compare July 9, 2026 11:28
Comment thread hibernate-core/src/main/java/org/hibernate/loader/internal/CacheLoadHelper.java Outdated
@jjennnn

jjennnn commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

@mbellade I tried removing the oldHolder.getEntity() fallback and reran the tests, but CircularityTest#testSelection started failing. It looks like the reuse logic is still needed for circular graph initialisation, so I kept it.

@mbellade mbellade left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jjennnn right, without the early return we probably need to maintain the lookup in CacheLoadHelper. The issue with that is, now we're doing a double map lookup in the negative case: when no holder is available, we are looking for one in both the multi-id entity loader and the 2nd level cache logic - with that said, the entityKey is pre-computed so that might be fine. The alternative would be to restore the early return in the generic 2nd level cache loader, and also return null there for deleted entries.

Signed-off-by: jjennnn <jennifer.joby@ucdconnect.ie>
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
79.6% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants