From e9a62232dcf6235154c826a4873d550ec8dbe4d5 Mon Sep 17 00:00:00 2001 From: Guenther Demetz Date: Tue, 7 May 2024 18:20:15 +0200 Subject: [PATCH] HHH-16557 Testcase and bugfix proposal (revised by beikov) --- .../action/internal/EntityDeleteAction.java | 2 +- .../internal/NaturalIdResolutionsImpl.java | 20 +++++++++-- .../engine/spi/NaturalIdResolutions.java | 8 +++-- .../entity/AbstractEntityPersister.java | 2 +- .../cached/CachedMutableNaturalIdTest.java | 34 +++++++++++++++++++ 5 files changed, 60 insertions(+), 6 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityDeleteAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityDeleteAction.java index e231c4d34e55..f28f79c66a82 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityDeleteAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityDeleteAction.java @@ -174,7 +174,7 @@ protected void postDeleteLoaded( final EntityKey key = entry.getEntityKey(); persistenceContext.removeEntityHolder( key ); removeCacheItem( ck ); - persistenceContext.getNaturalIdResolutions().removeSharedResolution( id, naturalIdValues, persister ); + persistenceContext.getNaturalIdResolutions().removeSharedResolution( id, naturalIdValues, persister, true); postDelete(); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdResolutionsImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdResolutionsImpl.java index 4960e9dd3a9f..e0eccd2d8c84 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdResolutionsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdResolutionsImpl.java @@ -433,6 +433,11 @@ private void manageSharedResolution( @Override public void removeSharedResolution(Object id, Object naturalId, EntityMappingType entityDescriptor) { + removeSharedResolution( id, naturalId, entityDescriptor, false ); + } + + @Override + public void removeSharedResolution(Object id, Object naturalId, EntityMappingType entityDescriptor, boolean delayToAfterTransactionCompletion) { final NaturalIdMapping naturalIdMapping = entityDescriptor.getNaturalIdMapping(); if ( naturalIdMapping == null ) { // nothing to do @@ -453,7 +458,18 @@ public void removeSharedResolution(Object id, Object naturalId, EntityMappingTyp final EntityPersister persister = locatePersisterForKey( entityDescriptor.getEntityPersister() ); final Object naturalIdCacheKey = cacheAccess.generateCacheKey( naturalId, persister, session() ); - cacheAccess.evict( naturalIdCacheKey ); + if ( delayToAfterTransactionCompletion ) { + session().asEventSource().getActionQueue().registerProcess( + (success, session) -> { + if ( success ) { + cacheAccess.evict( naturalIdCacheKey ); + } + } + ); + } + else { + cacheAccess.evict( naturalIdCacheKey ); + } // if ( sessionCachedNaturalIdValues != null // && !Arrays.equals( sessionCachedNaturalIdValues, deletedNaturalIdValues ) ) { @@ -479,7 +495,7 @@ public void handleSynchronization(Object pk, Object entity, EntityMappingType en cacheResolution( pk, naturalIdValuesFromCurrentObjectState, persister ); stashInvalidNaturalIdReference( persister, cachedNaturalIdValues ); - removeSharedResolution( pk, cachedNaturalIdValues, persister ); + removeSharedResolution( pk, cachedNaturalIdValues, persister, false ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/NaturalIdResolutions.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/NaturalIdResolutions.java index ee4727d23f08..19c945fa6c39 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/NaturalIdResolutions.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/NaturalIdResolutions.java @@ -68,8 +68,12 @@ void manageSharedResolution( /** * Removes any cross-reference from the L2 cache */ - void removeSharedResolution(Object id, Object naturalId, EntityMappingType entityDescriptor); - + void removeSharedResolution(Object id, Object naturalId, EntityMappingType entityDescriptor, boolean delayToAfterTransactionCompletion); + + default void removeSharedResolution(Object id, Object naturalId, EntityMappingType entityDescriptor) { + removeSharedResolution( id, naturalId, entityDescriptor, false ); + } + /** * Find the cached natural-id for the given identifier * diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 39bcabe75ea9..a646b2e2d37e 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -4115,7 +4115,7 @@ private void handleNaturalIdReattachment(Object entity, SharedSessionContractImp ? null : naturalIdMapping.extractNaturalIdFromEntityState( entitySnapshot ); - naturalIdResolutions.removeSharedResolution( id, naturalIdSnapshot, this ); + naturalIdResolutions.removeSharedResolution( id, naturalIdSnapshot, this, false ); final Object naturalId = naturalIdMapping.extractNaturalIdFromEntity( entity ); naturalIdResolutions.manageLocalResolution( id, naturalId, this, CachedNaturalIdValueSource.UPDATE ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/cached/CachedMutableNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/cached/CachedMutableNaturalIdTest.java index 999165354043..afded9f7ee96 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/cached/CachedMutableNaturalIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/cached/CachedMutableNaturalIdTest.java @@ -179,6 +179,40 @@ public void testBySimpleNaturalIdResolveEntityFrom2LCacheSubClass(SessionFactory } ); } + + @Test + @TestForIssue( jiraKey = "HHH-16557" ) + public void testCreateDeleteRecreate(SessionFactoryScope scope) { + + final Integer id = scope.fromTransaction( + (session) -> { + AllCached it = new AllCached( "it" ); + session.persist(it); + session.remove(it); + // insert-remove might happen in an app driven by users GUI interactions + return it.getId(); + } + ); + + // now recreate with same naturalId value + scope.inTransaction( + (session) -> { + AllCached it = new AllCached( "it" ); + session.persist(it); + // resolving from first level cache + assertNotNull(session.bySimpleNaturalId( AllCached.class ).load( "it" )); + } + ); + + scope.inTransaction( + (session) -> { + // should resolve from second level cache + final AllCached shouldBeThere = session.bySimpleNaturalId( AllCached.class ).load( "it" ); + assertNotNull( shouldBeThere ); + assert(id.compareTo(shouldBeThere.getId()) != 0); + } + ); + } @Test @JiraKey("HHH-16558")