/// <summary> Attempts to load the entity from the second-level cache. </summary> /// <param name="event">The load event </param> /// <param name="persister">The persister for the entity being requested for load </param> /// <param name="options">The load options. </param> /// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param> /// <returns> The entity from the second-level cache, or null. </returns> protected virtual async Task <object> LoadFromSecondLevelCacheAsync(LoadEvent @event, IEntityPersister persister, LoadType options, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ISessionImplementor source = @event.Session; bool useCache = persister.HasCache && source.CacheMode.HasFlag(CacheMode.Get) && @event.LockMode.LessThan(LockMode.Read); if (!useCache) { return(null); } ISessionFactoryImplementor factory = source.Factory; var batchSize = persister.GetBatchSize(); var entityBatch = source.PersistenceContext.BatchFetchQueue.QueryCacheQueue ?.GetEntityBatch(persister, @event.EntityId); if (entityBatch != null || batchSize > 1 && persister.Cache.PreferMultipleGet()) { // The first item in the array is the item that we want to load if (entityBatch != null) { if (entityBatch.Length == 0) { return(null); // The key was already checked } batchSize = entityBatch.Length; } if (entityBatch == null) { entityBatch = await(source.PersistenceContext.BatchFetchQueue.GetEntityBatchAsync(persister, @event.EntityId, batchSize, false, cancellationToken)).ConfigureAwait(false); } // Ignore null values as the retrieved batch may contains them when there are not enough // uninitialized entities in the queue var keys = new List <CacheKey>(batchSize); for (var i = 0; i < entityBatch.Length; i++) { var key = entityBatch[i]; if (key == null) { break; } keys.Add(source.GenerateCacheKey(key, persister.IdentifierType, persister.RootEntityName)); } var cachedObjects = await(persister.Cache.GetManyAsync(keys.ToArray(), source.Timestamp, cancellationToken)).ConfigureAwait(false); for (var i = 1; i < cachedObjects.Length; i++) { cancellationToken.ThrowIfCancellationRequested(); await(AssembleAsync( keys[i], cachedObjects[i], new LoadEvent(entityBatch[i], @event.EntityClassName, @event.LockMode, @event.Session), false)).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); return(await(AssembleAsync(keys[0], cachedObjects[0], @event, true)).ConfigureAwait(false)); } var cacheKey = source.GenerateCacheKey(@event.EntityId, persister.IdentifierType, persister.RootEntityName); var cachedObject = await(persister.Cache.GetAsync(cacheKey, source.Timestamp, cancellationToken)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); return(await(AssembleAsync(cacheKey, cachedObject, @event, true)).ConfigureAwait(false)); Task <object> AssembleAsync(CacheKey ck, object ce, LoadEvent evt, bool alterStatistics) { try { if (factory.Statistics.IsStatisticsEnabled && alterStatistics) { if (ce == null) { factory.StatisticsImplementor.SecondLevelCacheMiss(persister.Cache.RegionName); log.Debug("Entity cache miss: {0}", ck); } else { factory.StatisticsImplementor.SecondLevelCacheHit(persister.Cache.RegionName); log.Debug("Entity cache hit: {0}", ck); } } if (ce != null) { CacheEntry entry = (CacheEntry)persister.CacheEntryStructure.Destructure(ce, factory); // Entity was found in second-level cache... // NH: Different behavior (take a look to options.ExactPersister (NH-295)) if (!options.ExactPersister || persister.EntityMetamodel.SubclassEntityNames.Contains(entry.Subclass)) { return(AssembleCacheEntryAsync(entry, evt.EntityId, persister, evt, cancellationToken)); } } return(Task.FromResult <object>(null)); } catch (Exception ex) { return(Task.FromException <object>(ex)); } } }
private bool ShouldExecuteBatch(IEntityPersister persister, AbstractCacheBatch batch) { return(batch != _currentBatch || _currentPersister != persister || _currentBatch.BatchSize >= persister.GetBatchSize()); }
/// <summary> Attempts to load the entity from the second-level cache. </summary> /// <param name="event">The load event </param> /// <param name="persister">The persister for the entity being requested for load </param> /// <param name="options">The load options. </param> /// <returns> The entity from the second-level cache, or null. </returns> protected virtual object LoadFromSecondLevelCache(LoadEvent @event, IEntityPersister persister, LoadType options) { ISessionImplementor source = @event.Session; bool useCache = persister.HasCache && source.CacheMode.HasFlag(CacheMode.Get) && @event.LockMode.LessThan(LockMode.Read); if (!useCache) { return(null); } ISessionFactoryImplementor factory = source.Factory; var batchSize = persister.GetBatchSize(); if (batchSize > 1 && persister.Cache.PreferMultipleGet()) { // The first item in the array is the item that we want to load var entityBatch = source.PersistenceContext.BatchFetchQueue.GetEntityBatch(persister, @event.EntityId, batchSize, false); // Ignore null values as the retrieved batch may contains them when there are not enough // uninitialized entities in the queue var keys = new List <CacheKey>(batchSize); for (var i = 0; i < entityBatch.Length; i++) { var key = entityBatch[i]; if (key == null) { break; } keys.Add(source.GenerateCacheKey(key, persister.IdentifierType, persister.RootEntityName)); } var cachedObjects = persister.Cache.GetMany(keys.ToArray(), source.Timestamp); for (var i = 1; i < cachedObjects.Length; i++) { Assemble( keys[i], cachedObjects[i], new LoadEvent(entityBatch[i], @event.EntityClassName, @event.LockMode, @event.Session), false); } return(Assemble(keys[0], cachedObjects[0], @event, true)); } var cacheKey = source.GenerateCacheKey(@event.EntityId, persister.IdentifierType, persister.RootEntityName); var cachedObject = persister.Cache.Get(cacheKey, source.Timestamp); return(Assemble(cacheKey, cachedObject, @event, true)); object Assemble(CacheKey ck, object ce, LoadEvent evt, bool alterStatistics) { if (factory.Statistics.IsStatisticsEnabled && alterStatistics) { if (ce == null) { factory.StatisticsImplementor.SecondLevelCacheMiss(persister.Cache.RegionName); log.Debug("Entity cache miss: {0}", ck); } else { factory.StatisticsImplementor.SecondLevelCacheHit(persister.Cache.RegionName); log.Debug("Entity cache hit: {0}", ck); } } if (ce != null) { CacheEntry entry = (CacheEntry)persister.CacheEntryStructure.Destructure(ce, factory); // Entity was found in second-level cache... // NH: Different behavior (take a look to options.ExactPersister (NH-295)) if (!options.ExactPersister || persister.EntityMetamodel.SubclassEntityNames.Contains(entry.Subclass)) { return(AssembleCacheEntry(entry, evt.EntityId, persister, evt)); } } return(null); } }