Skip to content

fix: use merge() for in-session entities to prevent @PartitionKey NPE on re-attach - #160

Draft
r0goyal wants to merge 1 commit into
hibernate6-newfrom
fix/partition-key-hibernate6
Draft

fix: use merge() for in-session entities to prevent @PartitionKey NPE on re-attach#160
r0goyal wants to merge 1 commit into
hibernate6-newfrom
fix/partition-key-hibernate6

Conversation

@r0goyal

@r0goyal r0goyal commented May 1, 2026

Copy link
Copy Markdown
Collaborator

Problem

MultiTenantRelationalDao and MultiTenantLookupDao inner Shard.update() methods used an evict()+update() pattern to force a flush. When session.evict() removes an entity that was loaded in the same session,
Hibernate discards its loadedState snapshot. On flush, Hibernate 6.2+ AbstractMutationCoordinator.bindPartitionColumnValueBindings() accesses loadedState[I] with no null check → NPE for any entity annotated
with @PartitionKey.

Fix

In both DAOs, before falling back to evict+update, check if the entity is already managed:

   void update(T entity) {
       if (currentSession().contains(entity)) {
           currentSession().merge(entity);  // already managed — merge keeps loadedState intact
       } else {
           currentSession().evict(entity);  // detached — evict+update re-attach path
           currentSession().update(entity);
       }
   }

merge() on a managed entity propagates the state without evicting, keeping loadedState intact for the partition column binding at flush.

@sonarqubecloud

sonarqubecloud Bot commented May 1, 2026

Copy link
Copy Markdown

…nly=false for update ops

- Cherry-picked fix/skip-readonly-transaction-correct-paths (PR #158):
  wires skipReadOnlyTransaction correctly to single-query OpContexts only.

- In MultiTenantRelationalDao and MultiTenantLookupDao inner Shard.update():
  if the entity is already managed in the session, use session.merge() instead
  of evict+session.update(). Evicting a managed entity throws away its
  loadedState; re-attaching via update() leaves loadedState=null, which causes
  an NPE in Hibernate 6.2+ when @PartitionKey calls
  bindPartitionColumnValueBindings(). merge() on a managed entity copies state
  without destroying the session entry.

- Change GetAndUpdate, SelectAndUpdate (MultiTenantRelationalDao) and
  GetAndUpdateByLookupKey (MultiTenantLookupDao) execute() calls from
  readOnly=true to readOnly=false. Update transactions were running in
  read-only sessions, which disabled dirty-checking snapshots and caused
  updates to be silently lost. merge() on a read-only managed entity is
  still skipped at flush, so readOnly=false is required alongside the
  merge() change.

- Bump version to 2.1.12-HIBERNATE6-SNAPSHOT.

Verified: all 720 kisht-core tests pass (0 failures, 0 errors).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@r0goyal
r0goyal force-pushed the fix/partition-key-hibernate6 branch from 3ae6101 to 54db778 Compare August 12, 2026 15:05
@sonarqubecloud

Copy link
Copy Markdown

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.

1 participant