diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionAction.java index 3234fbc34a8a..2217f8256463 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionAction.java @@ -73,13 +73,13 @@ public final void beforeExecutions() throws CacheException { // the database. This action is responsible for second-level cache invalidation only. if ( persister.hasCache() ) { final var cache = persister.getCacheAccessStrategy(); - final Object ck = cache.generateCacheKey( + final Object cacheKey = cache.generateCacheKey( key, persister, session.getFactory(), session.getTenantIdentifier() ); - final SoftLock lock = cache.lockItem( session, ck, null ); + final SoftLock lock = cache.lockItem( session, cacheKey, null ); // the old behavior used key as opposed to getKey() afterTransactionProcess = new CacheCleanupProcess( key, persister, lock ); } @@ -129,13 +129,13 @@ protected final EventSource getSession() { protected final void evict() throws CacheException { if ( persister.hasCache() ) { final var cache = persister.getCacheAccessStrategy(); - final Object ck = cache.generateCacheKey( + final Object cacheKey = cache.generateCacheKey( key, persister, session.getFactory(), session.getTenantIdentifier() ); - cache.remove( session, ck); + cache.remove( session, cacheKey); } } @@ -170,13 +170,13 @@ private CacheCleanupProcess(Object key, CollectionPersister persister, SoftLock @Override public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) { final var cache = persister.getCacheAccessStrategy(); - final Object ck = cache.generateCacheKey( + final Object cacheKey = cache.generateCacheKey( key, persister, session.getFactory(), session.getTenantIdentifier() ); - cache.unlockItem( session, ck, lock ); + cache.unlockItem( session, cacheKey, lock ); } } 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 385e0a6d4e3e..6c1ed851b3ca 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 @@ -7,6 +7,7 @@ import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.cache.spi.access.SoftLock; +import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.PostCommitDeleteEventListener; @@ -106,9 +107,9 @@ public void execute() throws HibernateException { final boolean veto = isInstanceLoaded() && preDelete(); - handleNaturalIdResolutions( persister, session ); + handleNaturalIdLocalResolutions( persister, session.getPersistenceContextInternal() ); - final Object ck = lockCacheItem(); + final Object cacheKey = lockCacheItem(); if ( !isCascadeDeleteEnabled && !veto ) { final var eventMonitor = session.getEventMonitor(); @@ -124,11 +125,11 @@ public void execute() throws HibernateException { } if ( isInstanceLoaded() ) { - postDeleteLoaded( id, persister, session, instance, ck ); + postDeleteLoaded( id, persister, session, instance, cacheKey ); } else { // we're deleting an unloaded proxy - postDeleteUnloaded( id, persister, session, ck ); + postDeleteUnloaded( id, persister, session, cacheKey ); } final var statistics = session.getFactory().getStatistics(); @@ -137,10 +138,10 @@ public void execute() throws HibernateException { } } - private void handleNaturalIdResolutions(EntityPersister persister, EventSource session) { + private void handleNaturalIdLocalResolutions(EntityPersister persister, PersistenceContext context) { final var naturalIdMapping = persister.getNaturalIdMapping(); if ( naturalIdMapping != null ) { - naturalIdValues = session.getPersistenceContextInternal().getNaturalIdResolutions() + naturalIdValues = context.getNaturalIdResolutions() .removeLocalResolution( getId(), naturalIdMapping.extractNaturalIdFromEntityState( state ), @@ -166,7 +167,7 @@ protected void postDeleteLoaded( EntityPersister persister, SharedSessionContractImplementor session, Object instance, - Object ck) { + Object cacheKey) { // After actually deleting a row, record the fact that the instance no longer // exists on the database (needed for identity-column key generation), and // remove it from the session cache @@ -178,20 +179,24 @@ protected void postDeleteLoaded( entry.postDelete(); final var key = entry.getEntityKey(); persistenceContext.removeEntityHolder( key ); - removeCacheItem( ck ); + removeCacheItem( cacheKey ); persistenceContext.getNaturalIdResolutions() .removeSharedResolution( id, naturalIdValues, persister, true ); postDelete(); } - protected void postDeleteUnloaded(Object id, EntityPersister persister, SharedSessionContractImplementor session, Object ck) { + protected void postDeleteUnloaded( + Object id, + EntityPersister persister, + SharedSessionContractImplementor session, + Object cacheKey) { final var persistenceContext = session.getPersistenceContextInternal(); final var key = session.generateEntityKey( id, persister ); if ( !persistenceContext.containsDeletedUnloadedEntityKey( key ) ) { throw new AssertionFailure( "deleted proxy should be for an unloaded entity: " + key ); } persistenceContext.removeProxy( key ); - removeCacheItem( ck ); + removeCacheItem( cacheKey ); } protected boolean preDelete() { @@ -260,14 +265,14 @@ protected Object lockCacheItem() { if ( persister.canWriteToCache() ) { final var cache = persister.getCacheAccessStrategy(); final var session = getSession(); - final Object ck = cache.generateCacheKey( + final Object cacheKey = cache.generateCacheKey( getId(), persister, session.getFactory(), session.getTenantIdentifier() ); - lock = cache.lockItem( session, ck, getCurrentVersion() ); - return ck; + lock = cache.lockItem( session, cacheKey, getCurrentVersion() ); + return cacheKey; } else { return null; @@ -279,21 +284,21 @@ protected void unlockCacheItem() { if ( persister.canWriteToCache() ) { final var cache = persister.getCacheAccessStrategy(); final var session = getSession(); - final Object ck = cache.generateCacheKey( + final Object cacheKey = cache.generateCacheKey( getId(), persister, session.getFactory(), session.getTenantIdentifier() ); - cache.unlockItem( session, ck, lock ); + cache.unlockItem( session, cacheKey, lock ); } } - protected void removeCacheItem(Object ck) { + protected void removeCacheItem(Object cacheKey) { final var persister = getPersister(); if ( persister.canWriteToCache() ) { final var cache = persister.getCacheAccessStrategy(); - cache.remove( getSession(), ck ); + cache.remove( getSession(), cacheKey ); final var statistics = getSession().getFactory().getStatistics(); if ( statistics.isStatisticsEnabled() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java index c3a9efcbdb72..249c26d46219 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java @@ -22,6 +22,8 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.stat.internal.StatsHelper; +import static org.hibernate.cache.spi.entry.CacheEntryHelper.buildStructuredCacheEntry; + /** * The action for performing an entity insertion, for entities not defined to use {@code IDENTITY} generation. * @@ -163,11 +165,10 @@ protected void putCacheIfNecessary() { final var session = getSession(); if ( isCachePutEnabled( persister, session ) ) { final var factory = session.getFactory(); - final var cacheEntry = persister.buildCacheEntry( getInstance(), getState(), version, session ); - this.cacheEntry = persister.getCacheEntryStructure().structure( cacheEntry ); + cacheEntry = buildStructuredCacheEntry( getInstance(), version, getState(), persister, session ); final var cache = persister.getCacheAccessStrategy(); - final Object ck = cache.generateCacheKey( getId(), persister, factory, session.getTenantIdentifier() ); - final boolean put = cacheInsert( persister, ck ); + final Object cacheKey = cache.generateCacheKey( getId(), persister, factory, session.getTenantIdentifier() ); + final boolean put = cacheInsert( persister, cacheKey ); final var statistics = factory.getStatistics(); if ( put && statistics.isStatisticsEnabled() ) { @@ -179,7 +180,7 @@ protected void putCacheIfNecessary() { } } - protected boolean cacheInsert(EntityPersister persister, Object ck) { + protected boolean cacheInsert(EntityPersister persister, Object cacheKey) { final var session = getSession(); final var eventMonitor = session.getEventMonitor(); final var cachePutEvent = eventMonitor.beginCachePutEvent(); @@ -188,7 +189,7 @@ protected boolean cacheInsert(EntityPersister persister, Object ck) { boolean insert = false; try { eventListenerManager.cachePutStart(); - insert = cacheAccessStrategy.insert( session, ck, cacheEntry, version ); + insert = cacheAccessStrategy.insert( session, cacheKey, cacheEntry, version ); return insert; } finally { @@ -247,13 +248,14 @@ protected boolean preInsert() { } @Override - public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) throws HibernateException { + public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) + throws HibernateException { final var persister = getPersister(); if ( success && isCachePutEnabled( persister, getSession() ) ) { final var cache = persister.getCacheAccessStrategy(); final var factory = session.getFactory(); - final Object ck = cache.generateCacheKey( getId(), persister, factory, session.getTenantIdentifier() ); - final boolean put = cacheAfterInsert( cache, ck ); + final Object cacheKey = cache.generateCacheKey( getId(), persister, factory, session.getTenantIdentifier() ); + final boolean put = cacheAfterInsert( cache, cacheKey ); final var statistics = factory.getStatistics(); if ( put && statistics.isStatisticsEnabled() ) { @@ -266,7 +268,7 @@ public void doAfterTransactionCompletion(boolean success, SharedSessionContractI postCommitInsert( success ); } - protected boolean cacheAfterInsert(EntityDataAccess cache, Object ck) { + protected boolean cacheAfterInsert(EntityDataAccess cache, Object cacheKey) { final var session = getSession(); final var eventListenerManager = session.getEventListenerManager(); final var eventMonitor = session.getEventMonitor(); @@ -274,7 +276,7 @@ protected boolean cacheAfterInsert(EntityDataAccess cache, Object ck) { boolean afterInsert = false; try { eventListenerManager.cachePutStart(); - afterInsert = cache.afterInsert( session, ck, cacheEntry, version ); + afterInsert = cache.afterInsert( session, cacheKey, cacheEntry, version ); return afterInsert; } finally { diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java index b25d92bf9cc1..b78a40a87e94 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java @@ -27,6 +27,7 @@ import org.hibernate.stat.internal.StatsHelper; import org.hibernate.type.TypeHelper; +import static org.hibernate.cache.spi.entry.CacheEntryHelper.buildStructuredCacheEntry; import static org.hibernate.engine.internal.Versioning.getVersion; /** @@ -41,7 +42,6 @@ public class EntityUpdateAction extends EntityAction { private final boolean hasDirtyCollection; private final Object rowId; - private final NaturalIdMapping naturalIdMapping; private final Object previousNaturalIdValues; private Object nextVersion; @@ -83,22 +83,22 @@ public EntityUpdateAction( this.hasDirtyCollection = hasDirtyCollection; this.rowId = rowId; - naturalIdMapping = persister.getNaturalIdMapping(); - if ( naturalIdMapping == null ) { - previousNaturalIdValues = null; - } - else { - previousNaturalIdValues = - previousState == null - ? session.getPersistenceContextInternal().getNaturalIdSnapshot( id, persister ) - : naturalIdMapping.extractNaturalIdFromEntityState( previousState ); - session.getPersistenceContextInternal().getNaturalIdResolutions().manageLocalResolution( - id, - naturalIdMapping.extractNaturalIdFromEntityState( state ), - persister, - CachedNaturalIdValueSource.UPDATE - ); - } + final var naturalIdMapping = persister.getNaturalIdMapping(); + this.previousNaturalIdValues = + naturalIdMapping == null + ? null + : determinePreviousNaturalIdValues( persister, naturalIdMapping, id, previousState, session ); + } + + private static Object determinePreviousNaturalIdValues( + EntityPersister persister, + NaturalIdMapping naturalIdMapping, + Object id, + Object[] previousState, + SharedSessionContractImplementor session) { + return previousState == null + ? session.getPersistenceContextInternal().getNaturalIdSnapshot( id, persister ) + : naturalIdMapping.extractNaturalIdFromEntityState( previousState ); } protected Object[] getState() { @@ -121,7 +121,7 @@ protected boolean hasDirtyCollection() { } protected NaturalIdMapping getNaturalIdMapping() { - return naturalIdMapping; + return getPersister().getNaturalIdMapping(); } protected Object getPreviousNaturalIdValues() { @@ -145,10 +145,14 @@ public void execute() throws HibernateException { if ( !preUpdate() ) { final var persister = getPersister(); final var session = getSession(); + final var persistenceContext = session.getPersistenceContextInternal(); final Object id = getId(); final Object instance = getInstance(); + + handleNaturalIdLocalResolutions( id, persister, persistenceContext ); + final Object previousVersion = getPreviousVersion(); - final Object ck = lockCacheItem( previousVersion ); + final Object cacheKey = lockCacheItem( previousVersion ); final var eventMonitor = session.getEventMonitor(); final var event = eventMonitor.beginEntityUpdateEvent(); boolean success = false; @@ -170,15 +174,14 @@ public void execute() throws HibernateException { finally { eventMonitor.completeEntityUpdateEvent( event, id, persister.getEntityName(), success, session ); } - final var persistenceContext = session.getPersistenceContextInternal(); - final var entry = persistenceContext.getEntry( instance ); + final var entry = session.getPersistenceContextInternal().getEntry( instance ); if ( entry == null ) { throw new AssertionFailure( "possible non thread safe access to session" ); } handleGeneratedProperties( entry, generatedValues ); handleDeleted( entry ); - updateCacheItem( previousVersion, ck, entry ); - handleNaturalIdResolutions( persister, persistenceContext, id ); + updateCacheItem( previousVersion, cacheKey, entry ); + handleNaturalIdSharedResolutions( id, persister, persistenceContext ); postUpdate(); final var statistics = session.getFactory().getStatistics(); @@ -188,7 +191,20 @@ public void execute() throws HibernateException { } } - protected void handleNaturalIdResolutions(EntityPersister persister, PersistenceContext context, Object id) { + private void handleNaturalIdLocalResolutions(Object id, EntityPersister persister, PersistenceContext context) { + final var naturalIdMapping = persister.getNaturalIdMapping(); + if ( naturalIdMapping != null) { + context.getNaturalIdResolutions().manageLocalResolution( + id, + naturalIdMapping.extractNaturalIdFromEntityState( state ), + persister, + CachedNaturalIdValueSource.UPDATE + ); + } + } + + protected void handleNaturalIdSharedResolutions(Object id, EntityPersister persister, PersistenceContext context) { + final var naturalIdMapping = persister.getNaturalIdMapping(); if ( naturalIdMapping != null ) { context.getNaturalIdResolutions().manageSharedResolution( id, @@ -200,18 +216,17 @@ protected void handleNaturalIdResolutions(EntityPersister persister, Persistence } } - protected void updateCacheItem(Object previousVersion, Object ck, EntityEntry entry) { + protected void updateCacheItem(Object previousVersion, Object cacheKey, EntityEntry entry) { final var persister = getPersister(); if ( persister.canWriteToCache() ) { final var session = getSession(); if ( isCacheInvalidationRequired( persister, session ) || entry.getStatus() != Status.MANAGED ) { - persister.getCacheAccessStrategy().remove( session, ck ); + persister.getCacheAccessStrategy().remove( session, cacheKey ); } else if ( session.getCacheMode().isPutEnabled() ) { //TODO: inefficient if that cache is just going to ignore the updated state! - final var cacheEntry = persister.buildCacheEntry( getInstance(), state, nextVersion, getSession() ); - this.cacheEntry = persister.getCacheEntryStructure().structure( cacheEntry ); - final boolean put = updateCache( persister, previousVersion, ck ); + cacheEntry = buildStructuredCacheEntry( getInstance(), nextVersion, state, persister, session ); + final boolean put = updateCache( persister, previousVersion, cacheKey ); final var statistics = session.getFactory().getStatistics(); if ( put && statistics.isStatisticsEnabled() ) { @@ -298,21 +313,21 @@ protected Object lockCacheItem(Object previousVersion) { if ( persister.canWriteToCache() ) { final var session = getSession(); final var cache = persister.getCacheAccessStrategy(); - final Object ck = cache.generateCacheKey( + final Object cacheKey = cache.generateCacheKey( getId(), persister, session.getFactory(), session.getTenantIdentifier() ); - lock = cache.lockItem( session, ck, previousVersion ); - return ck; + lock = cache.lockItem( session, cacheKey, previousVersion ); + return cacheKey; } else { return null; } } - protected boolean updateCache(EntityPersister persister, Object previousVersion, Object ck) { + protected boolean updateCache(EntityPersister persister, Object previousVersion, Object cacheKey) { final var session = getSession(); final var eventMonitor = session.getEventMonitor(); final var cachePutEvent = eventMonitor.beginCachePutEvent(); @@ -321,7 +336,7 @@ protected boolean updateCache(EntityPersister persister, Object previousVersion, boolean update = false; try { eventListenerManager.cachePutStart(); - update = cacheAccessStrategy.update( session, ck, cacheEntry, nextVersion, previousVersion ); + update = cacheAccessStrategy.update( session, cacheKey, cacheEntry, nextVersion, previousVersion ); return update; } finally { @@ -400,7 +415,7 @@ private void updateCacheIfNecessary(boolean success, SharedSessionContractImplem if ( persister.canWriteToCache() ) { final var cache = persister.getCacheAccessStrategy(); final var factory = session.getFactory(); - final Object ck = cache.generateCacheKey( + final Object cacheKey = cache.generateCacheKey( getId(), persister, factory, @@ -408,10 +423,10 @@ private void updateCacheIfNecessary(boolean success, SharedSessionContractImplem ); if ( cacheUpdateRequired( success, persister, session ) ) { - cacheAfterUpdate( cache, ck, session); + cacheAfterUpdate( cache, cacheKey, session); } else { - cache.unlockItem( session, ck, lock ); + cache.unlockItem( session, cacheKey, lock ); } } } @@ -423,14 +438,14 @@ private boolean cacheUpdateRequired(boolean success, EntityPersister persister, && session.getCacheMode().isPutEnabled(); } - protected void cacheAfterUpdate(EntityDataAccess cache, Object ck, SharedSessionContractImplementor session) { + protected void cacheAfterUpdate(EntityDataAccess cache, Object cacheKey, SharedSessionContractImplementor session) { final var eventListenerManager = session.getEventListenerManager(); final var eventMonitor = session.getEventMonitor(); final var cachePutEvent = eventMonitor.beginCachePutEvent(); boolean put = false; try { eventListenerManager.cachePutStart(); - put = cache.afterUpdate( session, ck, cacheEntry, nextVersion, previousVersion, lock ); + put = cache.afterUpdate( session, cacheKey, cacheEntry, nextVersion, previousVersion, lock ); } finally { final var persister = getPersister(); @@ -451,7 +466,6 @@ protected void cacheAfterUpdate(EntityDataAccess cache, Object ck, SharedSession } eventListenerManager.cachePutEnd(); } - } } diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/CacheEntryHelper.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/CacheEntryHelper.java index 5b1af78c985d..e2475e390f90 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/CacheEntryHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/CacheEntryHelper.java @@ -6,15 +6,18 @@ import java.io.Serializable; +import org.hibernate.Internal; import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl; import org.hibernate.type.Type; /** * Operations for assembly and disassembly of an array of property values. */ -class CacheEntryHelper { +@Internal +public class CacheEntryHelper { /** * Apply the {@link Type#disassemble} operation across a series of values. @@ -27,7 +30,7 @@ class CacheEntryHelper { * * @return The disassembled state */ - public static Serializable[] disassemble( + static Serializable[] disassemble( final Object[] row, final Type[] types, final boolean[] nonCacheable, @@ -57,7 +60,7 @@ else if ( isPlaceholder( row[i] ) ) { * @param owner The entity "owning" the values * @return The assembled state */ - public static Object[] assemble( + static Object[] assemble( final Serializable[] row, final Type[] types, final SharedSessionContractImplementor session, @@ -79,4 +82,13 @@ private static boolean isPlaceholder(Object value) { || value == PropertyAccessStrategyBackRefImpl.UNKNOWN; } + public static Object buildStructuredCacheEntry( + Object entity, + Object version, + Object[] state, + EntityPersister persister, + SharedSessionContractImplementor session) { + return persister.getCacheEntryStructure() + .structure( persister.buildCacheEntry( entity, state, version, session ) ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryImpl.java index ed43e3dca642..8139f50b697f 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryImpl.java @@ -450,15 +450,16 @@ else if ( !persister.isMutable() ) { else { setStatus( MANAGED ); loadedState = persister.getValues( entity ); + final var context = getPersistenceContext(); TypeHelper.deepCopy( loadedState, persister.getPropertyTypes(), persister.getPropertyCheckability(), loadedState, - getPersistenceContext().getSession() + context.getSession() ); if ( persister.hasNaturalIdentifier() ) { - getPersistenceContext().getNaturalIdResolutions().manageLocalResolution( + context.getNaturalIdResolutions().manageLocalResolution( id, persister.getNaturalIdMapping().extractNaturalIdFromEntityState( loadedState ), persister, diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java index 2c8e9acaeeb8..2bce2ea75847 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java @@ -461,20 +461,20 @@ private Object lockAndLoad(LoadEvent event, EntityPersister persister, EntityKey final var cache = persister.getCacheAccessStrategy(); final SoftLock lock; - final Object ck; + final Object cacheKey; final boolean canWriteToCache = persister.canWriteToCache(); if ( canWriteToCache ) { - ck = cache.generateCacheKey( + cacheKey = cache.generateCacheKey( event.getEntityId(), persister, event.getFactory(), source.getTenantIdentifier() ); - lock = cache.lockItem( source, ck, null ); + lock = cache.lockItem( source, cacheKey, null ); } else { lock = null; - ck = null; + cacheKey = null; } final Object entity; @@ -483,7 +483,7 @@ private Object lockAndLoad(LoadEvent event, EntityPersister persister, EntityKey } finally { if ( canWriteToCache ) { - cache.unlockItem( source, ck, lock ); + cache.unlockItem( source, cacheKey, lock ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java index 797ed7220049..25fa264143e1 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java @@ -10,8 +10,6 @@ import org.hibernate.NonUniqueObjectException; import org.hibernate.TransientObjectException; import org.hibernate.UnresolvableObjectException; -import org.hibernate.cache.spi.access.CollectionDataAccess; -import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.access.SoftLock; import org.hibernate.engine.internal.Cascade; import org.hibernate.engine.internal.CascadePoint; @@ -25,7 +23,6 @@ import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.loader.ast.spi.CascadingFetchProfile; -import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.proxy.LazyInitializer; import org.hibernate.type.CollectionType; @@ -224,16 +221,16 @@ private static void evictEntity(Object object, EntityPersister persister, Object // multiple actions queued during the same flush previousVersion = persister.getVersion( object ); } - final EntityDataAccess cache = persister.getCacheAccessStrategy(); - final Object ck = cache.generateCacheKey( + final var cache = persister.getCacheAccessStrategy(); + final Object cacheKey = cache.generateCacheKey( id, persister, source.getFactory(), source.getTenantIdentifier() ); - final SoftLock lock = cache.lockItem( source, ck, previousVersion ); - cache.remove( source, ck ); - source.getActionQueue().registerProcess( (success, session) -> cache.unlockItem( session, ck, lock ) ); + final SoftLock lock = cache.lockItem( source, cacheKey, previousVersion ); + cache.remove( source, cacheKey ); + source.getActionQueue().registerProcess( (success, session) -> cache.unlockItem( session, cacheKey, lock ) ); } } @@ -330,19 +327,19 @@ private static void evictCachedCollections(Type[] types, Object id, EventSource final var metamodel = factory.getMappingMetamodel(); for ( Type type : types ) { if ( type instanceof CollectionType collectionType ) { - final CollectionPersister collectionPersister = + final var collectionPersister = metamodel.getCollectionDescriptor( collectionType.getRole() ); if ( collectionPersister.hasCache() ) { - final CollectionDataAccess cache = collectionPersister.getCacheAccessStrategy(); - final Object ck = cache.generateCacheKey( + final var cache = collectionPersister.getCacheAccessStrategy(); + final Object cacheKey = cache.generateCacheKey( id, collectionPersister, factory, source.getTenantIdentifier() ); - final SoftLock lock = cache.lockItem( source, ck, null ); - cache.remove( source, ck ); - actionQueue.registerProcess( (success, session) -> cache.unlockItem( session, ck, lock ) ); + final SoftLock lock = cache.lockItem( source, cacheKey, null ); + cache.remove( source, cacheKey ); + actionQueue.registerProcess( (success, session) -> cache.unlockItem( session, cacheKey, lock ) ); } } else if ( type instanceof ComponentType compositeType ) { diff --git a/hibernate-core/src/main/java/org/hibernate/internal/NaturalIdHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/NaturalIdHelper.java index d91b86081145..7c1146baec17 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/NaturalIdHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/NaturalIdHelper.java @@ -4,17 +4,12 @@ */ package org.hibernate.internal; -import org.hibernate.engine.spi.EntityEntry; -import org.hibernate.engine.spi.EntityKey; -import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.Status; import org.hibernate.id.IdentifierGenerationException; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.persister.entity.EntityPersister; -import java.util.Collection; - import static org.hibernate.engine.internal.NaturalIdLogging.NATURAL_ID_LOGGER; /** @@ -50,22 +45,19 @@ public static void performAnyNeededCrossReferenceSynchronizations( && entityMappingType.getNaturalIdMapping().isMutable() // skip synchronization when not in a transaction && session.isTransactionInProgress() ) { - final EntityPersister entityPersister = entityMappingType.getEntityPersister(); - final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); - final Collection cachedResolutions = - persistenceContext.getNaturalIdResolutions() - .getCachedPkResolutions( entityPersister ); + final var persister = entityMappingType.getEntityPersister(); + final var persistenceContext = session.getPersistenceContextInternal(); + final var naturalIdResolutions = persistenceContext.getNaturalIdResolutions(); final boolean loggerDebugEnabled = NATURAL_ID_LOGGER.isDebugEnabled(); - for ( Object id : cachedResolutions ) { - final EntityKey entityKey = session.generateEntityKey( id, entityPersister ); + for ( Object id : naturalIdResolutions.getCachedPkResolutions( persister ) ) { + final var entityKey = session.generateEntityKey( id, persister ); final Object entity = persistenceContext.getEntity( entityKey ); - final EntityEntry entry = persistenceContext.getEntry( entity ); + final var entry = persistenceContext.getEntry( entity ); if ( entry != null ) { if ( entry.requiresDirtyCheck( entity ) // MANAGED is the only status we care about here && entry.getStatus() == Status.MANAGED ) { - persistenceContext.getNaturalIdResolutions() - .handleSynchronization( id, entity, entityPersister ); + naturalIdResolutions.handleSynchronization( id, entity, persister ); } } else { diff --git a/hibernate-core/src/main/java/org/hibernate/internal/OptimisticLockHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/OptimisticLockHelper.java index 0f978e279b08..09e4d67749ef 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/OptimisticLockHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/OptimisticLockHelper.java @@ -8,15 +8,15 @@ import org.hibernate.action.spi.AfterTransactionCompletionProcess; import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.access.SoftLock; -import org.hibernate.cache.spi.entry.CacheEntry; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.Status; -import org.hibernate.event.monitor.spi.DiagnosticEvent; import org.hibernate.event.monitor.spi.EventMonitor; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.stat.internal.StatsHelper; +import static org.hibernate.cache.spi.entry.CacheEntryHelper.buildStructuredCacheEntry; + public final class OptimisticLockHelper { private OptimisticLockHelper() { @@ -24,12 +24,12 @@ private OptimisticLockHelper() { } public static void forceVersionIncrement(Object object, EntityEntry entry, SharedSessionContractImplementor session) { - final EntityPersister persister = entry.getPersister(); + final var persister = entry.getPersister(); final Object previousVersion = entry.getVersion(); SoftLock lock = null; final Object cacheKey; if ( persister.canWriteToCache() ) { - final EntityDataAccess cache = persister.getCacheAccessStrategy(); + final var cache = persister.getCacheAccessStrategy(); cacheKey = cache.generateCacheKey( entry.getId(), persister, @@ -64,15 +64,22 @@ public static void forceVersionIncrement(Object object, EntityEntry entry, Share } } - private static Object updateCacheItem(Object entity, Object previousVersion, Object nextVersion, Object ck, EntityEntry entry, EntityPersister persister, SharedSessionContractImplementor session) { + private static Object updateCacheItem( + Object entity, + Object previousVersion, + Object nextVersion, + Object cacheKey, + EntityEntry entry, + EntityPersister persister, + SharedSessionContractImplementor session) { if ( isCacheInvalidationRequired( persister, session ) || entry.getStatus() != Status.MANAGED ) { - persister.getCacheAccessStrategy().remove( session, ck ); + persister.getCacheAccessStrategy().remove( session, cacheKey ); } else if ( session.getCacheMode().isPutEnabled() ) { //TODO: inefficient if that cache is just going to ignore the updated state! - final CacheEntry ce = persister.buildCacheEntry( entity, entry.getLoadedState(), nextVersion, session ); - final Object cacheEntry = persister.getCacheEntryStructure().structure( ce ); - final boolean put = updateCache( persister, cacheEntry, previousVersion, nextVersion, ck, session ); + final Object cacheEntry = + buildStructuredCacheEntry( entity, nextVersion, entry.getLoadedState(), persister, session ); + final boolean put = updateCache( persister, cacheEntry, previousVersion, nextVersion, cacheKey, session ); final var statistics = session.getFactory().getStatistics(); if ( put && statistics.isStatisticsEnabled() ) { @@ -86,15 +93,21 @@ else if ( session.getCacheMode().isPutEnabled() ) { return null; } - private static boolean updateCache(EntityPersister persister, Object cacheEntry, Object previousVersion, Object nextVersion, Object ck, SharedSessionContractImplementor session) { - final EventMonitor eventMonitor = session.getEventMonitor(); - final DiagnosticEvent cachePutEvent = eventMonitor.beginCachePutEvent(); - final EntityDataAccess cacheAccessStrategy = persister.getCacheAccessStrategy(); + private static boolean updateCache( + EntityPersister persister, + Object cacheEntry, + Object previousVersion, + Object nextVersion, + Object cacheKey, + SharedSessionContractImplementor session) { + final var eventMonitor = session.getEventMonitor(); + final var cachePutEvent = eventMonitor.beginCachePutEvent(); + final var cacheAccessStrategy = persister.getCacheAccessStrategy(); final var eventListenerManager = session.getEventListenerManager(); boolean update = false; try { eventListenerManager.cachePutStart(); - update = cacheAccessStrategy.update( session, ck, cacheEntry, nextVersion, previousVersion ); + update = cacheAccessStrategy.update( session, cacheKey, cacheEntry, nextVersion, previousVersion ); return update; } finally { @@ -127,7 +140,13 @@ private static class CacheCleanupProcess implements AfterTransactionCompletionPr private final SoftLock lock; private final Object cacheEntry; - private CacheCleanupProcess(Object cacheKey, EntityPersister persister, Object previousVersion, Object nextVersion, SoftLock lock, Object cacheEntry) { + private CacheCleanupProcess( + Object cacheKey, + EntityPersister persister, + Object previousVersion, + Object nextVersion, + SoftLock lock, + Object cacheEntry) { this.cacheKey = cacheKey; this.persister = persister; this.previousVersion = previousVersion; @@ -138,7 +157,7 @@ private CacheCleanupProcess(Object cacheKey, EntityPersister persister, Object p @Override public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) { - final EntityDataAccess cache = persister.getCacheAccessStrategy(); + final var cache = persister.getCacheAccessStrategy(); if ( cacheUpdateRequired( success, persister, session ) ) { cacheAfterUpdate( cache, cacheKey, session ); } @@ -153,14 +172,14 @@ private static boolean cacheUpdateRequired(boolean success, EntityPersister pers && session.getCacheMode().isPutEnabled(); } - protected void cacheAfterUpdate(EntityDataAccess cache, Object ck, SharedSessionContractImplementor session) { + protected void cacheAfterUpdate(EntityDataAccess cache, Object cacheKey, SharedSessionContractImplementor session) { final var eventListenerManager = session.getEventListenerManager(); - final EventMonitor eventMonitor = session.getEventMonitor(); - final DiagnosticEvent cachePutEvent = eventMonitor.beginCachePutEvent(); + final var eventMonitor = session.getEventMonitor(); + final var cachePutEvent = eventMonitor.beginCachePutEvent(); boolean put = false; try { eventListenerManager.cachePutStart(); - put = cache.afterUpdate( session, ck, cacheEntry, nextVersion, previousVersion, lock ); + put = cache.afterUpdate( session, cacheKey, cacheEntry, nextVersion, previousVersion, lock ); } finally { eventMonitor.completeCachePutEvent( diff --git a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java index bc0d513c842f..5913fe0dbef2 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java @@ -319,7 +319,7 @@ public void delete(String entityName, Object entity) { if ( !firePreDelete(entity, id, persister) ) { getInterceptor().onDelete( entity, id, persister.getPropertyNames(), persister.getPropertyTypes() ); removeCollections( entity, id, persister ); - final Object ck = lockCacheItem( id, version, persister ); + final Object cacheKey = lockCacheItem( id, version, persister ); final var eventMonitor = getEventMonitor(); final var event = eventMonitor.beginEntityDeleteEvent(); boolean success = false; @@ -330,7 +330,7 @@ public void delete(String entityName, Object entity) { finally { eventMonitor.completeEntityDeleteEvent( event, id, persister.getEntityName(), success, this ); } - removeCacheItem( ck, persister ); + removeCacheItem( cacheKey, persister ); firePostDelete( entity, id, persister ); final var statistics = getStatistics(); if ( statistics.isStatisticsEnabled() ) { @@ -406,7 +406,7 @@ public void update(String entityName, Object entity) { } if ( !firePreUpdate(entity, id, state, persister) ) { getInterceptor().onUpdate( entity, id, state, persister.getPropertyNames(), persister.getPropertyTypes() ); - final Object ck = lockCacheItem( id, oldVersion, persister ); + final Object cacheKey = lockCacheItem( id, oldVersion, persister ); final var eventMonitor = getEventMonitor(); final var event = eventMonitor.beginEntityUpdateEvent(); boolean success = false; @@ -417,7 +417,7 @@ public void update(String entityName, Object entity) { finally { eventMonitor.completeEntityUpdateEvent( event, id, persister.getEntityName(), success, this ); } - removeCacheItem( ck, persister ); + removeCacheItem( cacheKey, persister ); removeAndRecreateCollections( entity, id, persister ); firePostUpdate( entity, id, state, persister ); final var statistics = getStatistics(); @@ -484,7 +484,7 @@ public void upsert(String entityName, Object entity) { if ( !firePreUpsert(entity, id, state, persister) ) { getInterceptor().onUpsert( entity, id, state, persister.getPropertyNames(), persister.getPropertyTypes() ); final Object oldVersion = versionToUpsert( entity, persister, state ); - final Object ck = lockCacheItem( id, oldVersion, persister ); + final Object cacheKey = lockCacheItem( id, oldVersion, persister ); final var eventMonitor = getEventMonitor(); final var event = eventMonitor.beginEntityUpsertEvent(); boolean success = false; @@ -495,7 +495,7 @@ public void upsert(String entityName, Object entity) { finally { eventMonitor.completeEntityUpsertEvent( event, id, persister.getEntityName(), success, this ); } - removeCacheItem( ck, persister ); + removeCacheItem( cacheKey, persister ); final var statistics = getStatistics(); if ( statistics.isStatisticsEnabled() ) { statistics.upsertEntity( persister.getEntityName() ); @@ -688,7 +688,7 @@ protected void forEachOwnedCollection( persister.visitAttributeMappings( attribute -> { if ( attribute.isPluralAttributeMapping() ) { final var descriptor = attribute.asPluralAttributeMapping().getCollectionDescriptor(); - final Object ck = lockCacheItem( key, descriptor ); + final Object cacheKey = lockCacheItem( key, descriptor ); if ( !descriptor.isInverse() ) { final Object value = attribute.getPropertyAccess().getGetter().get(entity); final PersistentCollection collection; @@ -706,7 +706,7 @@ protected void forEachOwnedCollection( } action.accept( descriptor, collection ); } - removeCacheItem( ck, descriptor ); + removeCacheItem( cacheKey, descriptor ); } } ); } @@ -915,13 +915,13 @@ public void refresh(String entityName, Object entity, LockMode lockMode) { if ( persister.canWriteToCache() ) { final var cacheAccess = persister.getCacheAccessStrategy(); if ( cacheAccess != null ) { - final Object ck = cacheAccess.generateCacheKey( + final Object cacheKey = cacheAccess.generateCacheKey( id, persister, getFactory(), getTenantIdentifier() ); - cacheAccess.evict( ck ); + cacheAccess.evict( cacheKey ); } } @@ -1362,48 +1362,48 @@ private LockMode getNullSafeLockMode(LockMode lockMode) { protected Object lockCacheItem(Object id, Object previousVersion, EntityPersister persister) { if ( persister.canWriteToCache() ) { final var cache = persister.getCacheAccessStrategy(); - final Object ck = cache.generateCacheKey( + final Object cacheKey = cache.generateCacheKey( id, persister, getFactory(), getTenantIdentifier() ); - final SoftLock lock = cache.lockItem( this, ck, previousVersion ); - afterCompletions.add( (success, session) -> cache.unlockItem( session, ck, lock ) ); - return ck; + final SoftLock lock = cache.lockItem( this, cacheKey, previousVersion ); + afterCompletions.add( (success, session) -> cache.unlockItem( session, cacheKey, lock ) ); + return cacheKey; } else { return null; } } - protected void removeCacheItem(Object ck, EntityPersister persister) { + protected void removeCacheItem(Object cacheKey, EntityPersister persister) { if ( persister.canWriteToCache() ) { - persister.getCacheAccessStrategy().remove( this, ck ); + persister.getCacheAccessStrategy().remove( this, cacheKey ); } } protected Object lockCacheItem(Object key, CollectionPersister persister) { if ( persister.hasCache() ) { final var cache = persister.getCacheAccessStrategy(); - final Object ck = cache.generateCacheKey( + final Object cacheKey = cache.generateCacheKey( key, persister, getFactory(), getTenantIdentifier() ); - final SoftLock lock = cache.lockItem( this, ck, null ); - afterCompletions.add( (success, session) -> cache.unlockItem( this, ck, lock ) ); - return ck; + final SoftLock lock = cache.lockItem( this, cacheKey, null ); + afterCompletions.add( (success, session) -> cache.unlockItem( this, cacheKey, lock ) ); + return cacheKey; } else { return null; } } - protected void removeCacheItem(Object ck, CollectionPersister persister) { + protected void removeCacheItem(Object cacheKey, CollectionPersister persister) { if ( persister.hasCache() ) { - persister.getCacheAccessStrategy().remove( this, ck ); + persister.getCacheAccessStrategy().remove( this, cacheKey ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java index bac6c670fdc5..7444ff4e5f8f 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java @@ -10,22 +10,18 @@ import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.ObjectDeletedException; -import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.access.SoftLock; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SubselectFetch; -import org.hibernate.event.monitor.spi.EventMonitor; import org.hibernate.event.spi.EventSource; -import org.hibernate.event.monitor.spi.DiagnosticEvent; import org.hibernate.internal.OptimisticLockHelper; import org.hibernate.internal.build.AllowReflection; import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.JdbcMapping; -import org.hibernate.persister.entity.EntityPersister; import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl; @@ -61,7 +57,7 @@ public static void upgradeLock( final LockMode requestedLockMode = lockOptions.getLockMode(); if ( requestedLockMode.greaterThan( entry.getLockMode() ) ) { // Request is for a more restrictive lock than the lock already held - final EntityPersister persister = entry.getPersister(); + final var persister = entry.getPersister(); if ( entry.getStatus().isDeletedOrGone()) { throw new ObjectDeletedException( @@ -82,12 +78,12 @@ public static void upgradeLock( final boolean cachingEnabled = persister.canWriteToCache(); SoftLock lock = null; - Object ck = null; + Object cacheKey = null; try { if ( cachingEnabled ) { - final EntityDataAccess cache = persister.getCacheAccessStrategy(); - ck = cache.generateCacheKey( entry.getId(), persister, session.getFactory(), session.getTenantIdentifier() ); - lock = cache.lockItem( session, ck, entry.getVersion() ); + final var cache = persister.getCacheAccessStrategy(); + cacheKey = cache.generateCacheKey( entry.getId(), persister, session.getFactory(), session.getTenantIdentifier() ); + lock = cache.lockItem( session, cacheKey, entry.getVersion() ); } if ( persister.isVersioned() && entry.getVersion() == null ) { @@ -110,8 +106,8 @@ public static void upgradeLock( OptimisticLockHelper.forceVersionIncrement( object, entry, session ); } else if ( entry.isExistsInDatabase() ) { - final EventMonitor eventMonitor = session.getEventMonitor(); - final DiagnosticEvent entityLockEvent = eventMonitor.beginEntityLockEvent(); + final var eventMonitor = session.getEventMonitor(); + final var entityLockEvent = eventMonitor.beginEntityLockEvent(); boolean success = false; try { persister.lock( entry.getId(), entry.getVersion(), object, lockOptions, session ); @@ -134,7 +130,7 @@ else if ( entry.isExistsInDatabase() ) { // the database now holds a lock + the object is flushed from the cache, // so release the soft lock if ( cachingEnabled ) { - persister.getCacheAccessStrategy().unlockItem( session, ck, lock ); + persister.getCacheAccessStrategy().unlockItem( session, cacheKey, lock ); } } } 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 b711e4a918a3..059b0f09143f 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 @@ -3970,10 +3970,11 @@ public Boolean isTransient(Object entity, SharedSessionContractImplementor sessi // check to see if it is in the second-level cache if ( session.getCacheMode().isGetEnabled() && canReadFromCache() ) { - final EntityDataAccess cache = getCacheAccessStrategy(); - final Object ck = cache.generateCacheKey( id, this, session.getFactory(), session.getTenantIdentifier() ); - final Object ce = CacheHelper.fromSharedCache( session, ck, this, getCacheAccessStrategy() ); - if ( ce != null ) { + final var cache = getCacheAccessStrategy(); + final Object cacheKey = + cache.generateCacheKey( id, this, session.getFactory(), session.getTenantIdentifier() ); + final Object cacheEntry = CacheHelper.fromSharedCache( session, cacheKey, this, getCacheAccessStrategy() ); + if ( cacheEntry != null ) { return false; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResultsHelper.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResultsHelper.java index b10f882f62e8..349f7074b62e 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResultsHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResultsHelper.java @@ -6,30 +6,21 @@ import org.hibernate.CacheMode; -import org.hibernate.cache.spi.access.CollectionDataAccess; +import org.hibernate.SharedSessionContract; import org.hibernate.cache.spi.entry.CollectionCacheEntry; import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.engine.spi.BatchFetchQueue; import org.hibernate.engine.spi.CollectionEntry; -import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.PersistenceContext; -import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.event.monitor.spi.EventMonitor; -import org.hibernate.event.monitor.spi.DiagnosticEvent; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; -import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.persister.collection.CollectionPersister; -import org.hibernate.persister.entity.EntityPersister; import org.hibernate.sql.results.jdbc.spi.JdbcValues; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMapping; -import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingResolution; import org.hibernate.sql.results.spi.RowReader; import org.hibernate.sql.results.spi.RowTransformer; -import org.hibernate.stat.spi.StatisticsImplementor; -import org.hibernate.type.EntityType; import static org.hibernate.pretty.MessageHelper.collectionInfoString; @@ -53,9 +44,8 @@ public static RowReader createRowReader( RowTransformer rowTransformer, Class transformedResultJavaType, JdbcValuesMapping jdbcValuesMapping) { - final JdbcValuesMappingResolution jdbcValuesMappingResolution = jdbcValuesMapping.resolveAssemblers( sessionFactory ); return new StandardRowReader<>( - jdbcValuesMappingResolution, + jdbcValuesMapping.resolveAssemblers( sessionFactory ), rowTransformer, transformedResultJavaType ); @@ -64,81 +54,95 @@ public static RowReader createRowReader( public static void finalizeCollectionLoading( PersistenceContext persistenceContext, CollectionPersister collectionDescriptor, - PersistentCollection collectionInstance, + PersistentCollection collection, Object key, boolean hasNoQueuedAdds) { - final SharedSessionContractImplementor session = persistenceContext.getSession(); - - CollectionEntry collectionEntry = persistenceContext.getCollectionEntry( collectionInstance ); - if ( collectionEntry == null ) { - collectionEntry = persistenceContext.addInitializedCollection( collectionDescriptor, collectionInstance, key ); - } - else { - collectionEntry.postInitialize( collectionInstance, session ); - } - + final var session = persistenceContext.getSession(); + final var collectionEntry = + initializedEntry( persistenceContext, collectionDescriptor, collection, key, session ); if ( collectionDescriptor.getCollectionType().hasHolder() ) { - // in case of PersistentArrayHolder we have to realign - // the EntityEntry loaded state with the entity values - final Object owner = collectionInstance.getOwner(); - final EntityEntry entry = persistenceContext.getEntry( owner ); - final Object[] loadedState = entry.getLoadedState(); - if ( loadedState != null ) { - final PluralAttributeMapping mapping = collectionDescriptor.getAttributeMapping(); - final int propertyIndex = mapping.getStateArrayPosition(); - loadedState[propertyIndex] = mapping.getValue( owner ); - } - // else it must be an immutable entity or loaded in read-only mode, - // but unfortunately we have no way to reliably determine that here - persistenceContext.addCollectionHolder( collectionInstance ); + addCollectionHolder( persistenceContext, collectionDescriptor, collection ); } - - final BatchFetchQueue batchFetchQueue = persistenceContext.getBatchFetchQueue(); - batchFetchQueue.removeBatchLoadableCollection( collectionEntry ); - - // add to cache if: - final boolean addToCache = - // there were no queued additions - hasNoQueuedAdds - // and the role has a cache - && collectionDescriptor.hasCache() - // and this is not a forced initialization during flush - && session.getCacheMode().isPutEnabled() && !collectionEntry.isDoremove(); - if ( addToCache ) { - addCollectionToCache( persistenceContext, collectionDescriptor, collectionInstance, key ); + persistenceContext.getBatchFetchQueue().removeBatchLoadableCollection( collectionEntry ); + if ( addToCache( session, collectionEntry, collectionDescriptor, hasNoQueuedAdds ) ) { + addCollectionToCache( persistenceContext, collectionDescriptor, collection, key ); + } + final var statistics = session.getFactory().getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.loadCollection( collectionDescriptor.getRole() ); } if ( LOG.isTraceEnabled() ) { LOG.trace( "Collection fully initialized: " - + collectionInfoString( collectionDescriptor, collectionInstance, key, session ) ); - } - - final StatisticsImplementor statistics = session.getFactory().getStatistics(); - if ( statistics.isStatisticsEnabled() ) { - statistics.loadCollection( collectionDescriptor.getRole() ); + + collectionInfoString( collectionDescriptor, collection, key, session ) ); } // todo (6.0) : there is other logic still needing to be implemented here. caching, etc // see org.hibernate.engine.loading.internal.CollectionLoadContext#endLoadingCollection in 5.x } + private static boolean addToCache( + SharedSessionContract session, + CollectionEntry collectionEntry, + CollectionPersister collectionDescriptor, + boolean hasNoQueuedAdds) { + return hasNoQueuedAdds // there were no queued additions + && collectionDescriptor.hasCache() // the collection role has a cache + && session.getCacheMode().isPutEnabled() // the session cache mode allows puts + && !collectionEntry.isDoremove(); // this is not a forced initialization during flush + } + + private static void addCollectionHolder( + PersistenceContext persistenceContext, + CollectionPersister collectionDescriptor, + PersistentCollection collectionInstance) { + // in case of PersistentArrayHolder we have to realign + // the EntityEntry loaded state with the entity values + final Object owner = collectionInstance.getOwner(); + final var loadedState = persistenceContext.getEntry( owner ).getLoadedState(); + if ( loadedState != null ) { + final var mapping = collectionDescriptor.getAttributeMapping(); + final int propertyIndex = mapping.getStateArrayPosition(); + loadedState[propertyIndex] = mapping.getValue( owner ); + } + // else it must be an immutable entity or loaded in read-only mode, + // but, unfortunately, we have no way to reliably determine that here + persistenceContext.addCollectionHolder( collectionInstance ); + } + + private static CollectionEntry initializedEntry( + PersistenceContext context, + CollectionPersister collectionDescriptor, + PersistentCollection collectionInstance, + Object key, + SharedSessionContractImplementor session) { + final var collectionEntry = context.getCollectionEntry( collectionInstance ); + if ( collectionEntry == null ) { + return context.addInitializedCollection( collectionDescriptor, collectionInstance, key ); + } + else { + collectionEntry.postInitialize( collectionInstance, session ); + return collectionEntry; + } + } + /** * Add the collection to the second-level cache */ private static void addCollectionToCache( PersistenceContext persistenceContext, CollectionPersister collectionDescriptor, - PersistentCollection collectionInstance, + PersistentCollection collection, Object key) { - final SharedSessionContractImplementor session = persistenceContext.getSession(); - final SessionFactoryImplementor factory = session.getFactory(); + final var session = persistenceContext.getSession(); if ( LOG.isTraceEnabled() ) { LOG.trace( "Caching collection: " - + collectionInfoString( collectionDescriptor, collectionInstance, key, session ) ); + + collectionInfoString( collectionDescriptor, collection, key, session ) ); } - if ( session.getLoadQueryInfluencers().hasEnabledFilters() && collectionDescriptor.isAffectedByEnabledFilters( session ) ) { + if ( session.getLoadQueryInfluencers().hasEnabledFilters() + && collectionDescriptor.isAffectedByEnabledFilters( session ) ) { // some filters affecting the collection are enabled on the session, so do not do the put into the cache. LOG.debug( "Refusing to add to cache due to enabled filters" ); // todo : add the notion of enabled filters to the cache key to differentiate filtered collections from non-filtered; @@ -150,24 +154,11 @@ private static void addCollectionToCache( final Object version; if ( collectionDescriptor.isVersioned() ) { - Object collectionOwner = persistenceContext.getCollectionOwner( key, collectionDescriptor ); + final Object collectionOwner = + getCollectionOwner( persistenceContext, collectionDescriptor, collection, key, session ); if ( collectionOwner == null ) { - // generally speaking this would be caused by the collection key being defined by a property-ref, thus - // the collection key and the owner key would not match up. In this case, try to use the key of the - // owner instance associated with the collection itself, if one. If the collection does already know - // about its owner, that owner should be the same instance as associated with the PC, but we do the - // resolution against the PC anyway just to be safe since the lookup should not be costly. - if ( collectionInstance != null ) { - final Object linkedOwner = collectionInstance.getOwner(); - if ( linkedOwner != null ) { - final Object ownerKey = collectionDescriptor.getOwnerEntityPersister().getIdentifier( linkedOwner, session ); - collectionOwner = persistenceContext.getCollectionOwner( ownerKey, collectionDescriptor ); - } - } - if ( collectionOwner == null ) { - LOG.debugf( "Unable to resolve owner of loading collection for second level caching. Refusing to add to cache."); - return; - } + LOG.debug( "Unable to resolve owner of loading collection for second level caching. Refusing to add to cache."); + return; } version = persistenceContext.getEntry( collectionOwner ).getVersion(); } @@ -175,8 +166,19 @@ private static void addCollectionToCache( version = null; } - final CollectionCacheEntry entry = new CollectionCacheEntry( collectionInstance, collectionDescriptor ); - final CollectionDataAccess cacheAccess = collectionDescriptor.getCacheAccessStrategy(); + addCollectionToCache( persistenceContext, collectionDescriptor, collection, key, version ); + } + + private static void addCollectionToCache( + PersistenceContext context, + CollectionPersister collectionDescriptor, + PersistentCollection collection, + Object key, + Object version) { + final var session = context.getSession(); + final var factory = session.getFactory(); + final var entry = new CollectionCacheEntry( collection, collectionDescriptor ); + final var cacheAccess = collectionDescriptor.getCacheAccessStrategy(); final Object cacheKey = cacheAccess.generateCacheKey( key, collectionDescriptor, @@ -184,22 +186,11 @@ private static void addCollectionToCache( session.getTenantIdentifier() ); - boolean isPutFromLoad = true; - if ( collectionDescriptor.getElementType() instanceof EntityType ) { - final EntityPersister entityPersister = collectionDescriptor.getElementPersister(); - for ( Object id : entry.getState() ) { - if ( persistenceContext.wasInsertedDuringTransaction( entityPersister, id ) ) { - isPutFromLoad = false; - break; - } - } - } - // CollectionRegionAccessStrategy has no update, so avoid putting uncommitted data via putFromLoad - if ( isPutFromLoad ) { - final SessionEventListenerManager eventListenerManager = session.getEventListenerManager(); - final EventMonitor eventMonitor = session.getEventMonitor(); - final DiagnosticEvent cachePutEvent = eventMonitor.beginCachePutEvent(); + if ( isPutFromLoad( context, collectionDescriptor, entry ) ) { + final var eventListenerManager = session.getEventListenerManager(); + final var eventMonitor = session.getEventMonitor(); + final var cachePutEvent = eventMonitor.beginCachePutEvent(); boolean put = false; try { eventListenerManager.cachePutStart(); @@ -209,7 +200,7 @@ private static void addCollectionToCache( collectionDescriptor.getCacheEntryStructure().structure( entry ), version, factory.getSessionFactoryOptions().isMinimalPutsEnabled() - && session.getCacheMode()!= CacheMode.REFRESH + && session.getCacheMode() != CacheMode.REFRESH ); } finally { @@ -223,15 +214,58 @@ private static void addCollectionToCache( ); eventListenerManager.cachePutEnd(); - final StatisticsImplementor statistics = factory.getStatistics(); + final var statistics = factory.getStatistics(); if ( put && statistics.isStatisticsEnabled() ) { statistics.collectionCachePut( collectionDescriptor.getNavigableRole(), - collectionDescriptor.getCacheAccessStrategy().getRegion().getName() + cacheAccess.getRegion().getName() ); } + } + } + } + + private static boolean isPutFromLoad( + PersistenceContext context, + CollectionPersister collectionDescriptor, + CollectionCacheEntry entry) { + if ( collectionDescriptor.getElementType().isEntityType() ) { + final var entityPersister = collectionDescriptor.getElementPersister(); + for ( Object id : entry.getState() ) { + if ( context.wasInsertedDuringTransaction( entityPersister, id ) ) { + return false; + } + } + } + return true; + } + private static Object getCollectionOwner( + PersistenceContext context, + CollectionPersister collectionDescriptor, + PersistentCollection collection, + Object key, + SharedSessionContractImplementor session) { + final Object collectionOwner = context.getCollectionOwner( key, collectionDescriptor ); + if ( collectionOwner == null ) { + // This happens when the collection key is defined by a property-ref. In this case, the collection key + // and the owner key would not match up. Use the key of the owner instance associated with the collection + // itself, if there is one. If the collection does already know about its owner, that owner should be the + // same instance as associated with the PC, but we do the resolution against the PC anyway just to be safe, + // since the lookup should not be costly. + if ( collection != null ) { + final Object linkedOwner = collection.getOwner(); + if ( linkedOwner != null ) { + final Object ownerKey = + collectionDescriptor.getOwnerEntityPersister() + .getIdentifier( linkedOwner, session ); + return context.getCollectionOwner( ownerKey, collectionDescriptor ); + } } + return null; + } + else { + return collectionOwner; } } }