/// <summary></summary>
		public override void Execute()
		{
			CacheKey ck = null;
			if (Persister.HasCache)
			{
				ck = new CacheKey(
					Id,
					Persister.IdentifierType,
					(string) Persister.IdentifierSpace,
					Session.Factory
					);
				_lock = Persister.Cache.Lock(ck, lastVersion);
			}
			Persister.Update(Id, state, dirtyFields, hasDirtyCollection, previousState, lastVersion, Instance, Session);
			
			EntityEntry entry = Session.GetEntry(Instance);
			if (entry == null)
			{
				throw new AssertionFailure("possible nonthreadsafe access to session");
			}

			if (entry.Status == Status.Loaded || Persister.IsVersionPropertyGenerated)
			{
				// get the updated snapshot of the entity state by cloning current state;
				// it is safe to copy in place, since by this time no-one else (should have)
				// has a reference  to the array
				TypeFactory.DeepCopy(
						state,
						Persister.PropertyTypes,
						Persister.PropertyCheckability,
						state);
				if (Persister.HasUpdateGeneratedProperties)
				{
					// this entity defines proeprty generation, so process those generated
					// values...
					Persister.ProcessUpdateGeneratedProperties(Id, Instance, state, Session);
					if (Persister.IsVersionPropertyGenerated)
					{
						nextVersion = Versioning.GetVersion(state, Persister);
					}
				}
				// have the entity entry perform post-update processing, passing it the
				// update state and the new version (if one).
				entry.PostUpdate(Instance, state, nextVersion);
			}

			if (Persister.HasCache)
			{
				if (Persister.IsCacheInvalidationRequired || entry.Status != Status.Loaded)
				{
					Persister.Cache.Evict(ck);
				}
				else
				{
					// TODO: Inefficient if that cache is just going to ignore the updated state!
					cacheEntry = new CacheEntry(Instance, Persister, Session);
					Persister.Cache.Update(ck, cacheEntry);
				}
			}
		}
Example #2
0
        public override void AfterTransactionCompletion(bool success)
        {
            IEntityPersister persister = Persister;
            if (persister.HasCache)
            {
                CacheKey ck = new CacheKey(Id, persister.IdentifierType, persister.RootEntityName, Session.EntityMode, Session.Factory);

                if (success && cacheEntry != null)
                {
                    bool put = persister.Cache.AfterUpdate(ck, cacheEntry, nextVersion, slock);

                    if (put && Session.Factory.Statistics.IsStatisticsEnabled)
                    {
                        Session.Factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName);
                    }
                }
                else
                {
                    persister.Cache.Release(ck, slock);
                }
            }
            if (success)
            {
                PostCommitUpdate();
            }
        }
		public bool Put(CacheKey key, object value, long timestamp, object version, IComparer versionComparator,
		                bool minimalPut)
		{
			if (timestamp == long.MinValue)
			{
				// MinValue means cache is disabled
				return false;
			}

			lock (lockObject)
			{
				if (minimalPut && cache.Get(key) != null)
				{
					if (log.IsDebugEnabled)
					{
						log.Debug("item already cached: " + key);
					}
					return false;
				}
				if (log.IsDebugEnabled)
				{
					log.Debug("Caching: " + key);
				}
				cache.Put(key, value);
				return true;
			}
		}
		/// <summary> Try to initialize a collection from the cache</summary>
		private bool InitializeCollectionFromCache(object id, ICollectionPersister persister, IPersistentCollection collection, ISessionImplementor source)
		{

			if (!(source.EnabledFilters.Count == 0) && persister.IsAffectedByEnabledFilters(source))
			{
				log.Debug("disregarding cached version (if any) of collection due to enabled filters ");
				return false;
			}

			bool useCache = persister.HasCache && ((source.CacheMode & CacheMode.Get) == CacheMode.Get);

			if (!useCache)
			{
				return false;
			}
			else
			{
				ISessionFactoryImplementor factory = source.Factory;

				CacheKey ck = new CacheKey(id, persister.KeyType, persister.Role, source.EntityMode, factory);
				object ce = persister.Cache.Get(ck, source.Timestamp);

				if (factory.Statistics.IsStatisticsEnabled)
				{
					if (ce == null)
					{
						factory.StatisticsImplementor.SecondLevelCacheMiss(persister.Cache.RegionName);
					}
					else
					{
						factory.StatisticsImplementor.SecondLevelCacheHit(persister.Cache.RegionName);
					}
				}

				if (ce == null)
				{
					log.DebugFormat("Collection cache miss: {0}", ck);
				}
				else
				{
					log.DebugFormat("Collection cache hit: {0}", ck);
				}

				if (ce == null)
				{
					return false;
				}
				else
				{
					IPersistenceContext persistenceContext = source.PersistenceContext;

					CollectionCacheEntry cacheEntry = (CollectionCacheEntry)persister.CacheEntryStructure.Destructure(ce, factory);
					cacheEntry.Assemble(collection, persister, persistenceContext.GetCollectionOwner(id, persister));

					persistenceContext.GetCollectionEntry(collection).PostInitialize(collection);
					return true;
				}
			}
		}
Example #5
0
		public override void Execute()
		{
			IEntityPersister persister = Persister;
			ISessionImplementor session = Session;
			object instance = Instance;
			object id = Id;

			bool veto = PreInsert();

			// Don't need to lock the cache here, since if someone
			// else inserted the same pk first, the insert would fail
			if (!veto)
			{

				persister.Insert(id, state, instance, Session);

				EntityEntry entry = Session.PersistenceContext.GetEntry(instance);
				if (entry == null)
				{
					throw new AssertionFailure("Possible nonthreadsafe access to session");
				}

				entry.PostInsert();

				if (persister.HasInsertGeneratedProperties)
				{
					persister.ProcessInsertGeneratedProperties(id, instance, state, Session);
					if (persister.IsVersionPropertyGenerated)
					{
						version = Versioning.GetVersion(state, persister);
					}
					entry.PostUpdate(instance, state, version);
				}
			}

			ISessionFactoryImplementor factory = Session.Factory;

			if (IsCachePutEnabled(persister))
			{
				CacheEntry ce = new CacheEntry(state, persister, persister.HasUninitializedLazyProperties(instance, session.EntityMode), version, session, instance);
				cacheEntry = persister.CacheEntryStructure.Structure(ce);

				CacheKey ck = new CacheKey(id, persister.IdentifierType, persister.RootEntityName, Session.EntityMode, Session.Factory);
				bool put = persister.Cache.Insert(ck, cacheEntry, version);

				if (put && factory.Statistics.IsStatisticsEnabled)
				{
					factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName);
				}
			}

			PostInsert();

			if (factory.Statistics.IsStatisticsEnabled && !veto)
			{
				factory.StatisticsImplementor.InsertEntity(Persister.EntityName);
			}
		}
		public object Get(CacheKey key, long timestamp)
		{
			object result = cache.Get(key);
			if (result != null && log.IsDebugEnabled)
			{
				log.Debug("Cache hit: " + key);
			}
			return result;	
		}
		/// <summary> 
		/// Performs a pessimistic lock upgrade on a given entity, if needed. 
		/// </summary>
		/// <param name="entity">The entity for which to upgrade the lock.</param>
		/// <param name="entry">The entity's EntityEntry instance.</param>
		/// <param name="requestedLockMode">The lock mode being requested for locking. </param>
		/// <param name="source">The session which is the source of the event being processed.</param>
		protected virtual void UpgradeLock(object entity, EntityEntry entry, LockMode requestedLockMode, ISessionImplementor source)
		{
			if (requestedLockMode.GreaterThan(entry.LockMode))
			{
				// The user requested a "greater" (i.e. more restrictive) form of
				// pessimistic lock
				if (entry.Status != Status.Loaded)
				{
					throw new ObjectDeletedException("attempted to lock a deleted instance", entry.Id, entry.EntityName);
				}

				IEntityPersister persister = entry.Persister;

				if (log.IsDebugEnabled)
				{
					log.Debug(string.Format("locking {0} in mode: {1}", MessageHelper.InfoString(persister, entry.Id, source.Factory), requestedLockMode));
				}

				ISoftLock slock;
				CacheKey ck;
				if (persister.HasCache)
				{
					ck = new CacheKey(entry.Id, persister.IdentifierType, persister.RootEntityName, source.EntityMode, source.Factory);
					slock = persister.Cache.Lock(ck, entry.Version);
				}
				else
				{
					ck = null;
					slock = null;
				}

				try
				{
					if (persister.IsVersioned && requestedLockMode == LockMode.Force)
					{
						// todo : should we check the current isolation mode explicitly?
						object nextVersion = persister.ForceVersionIncrement(entry.Id, entry.Version, source);
						entry.ForceLocked(entity, nextVersion);
					}
					else
					{
						persister.Lock(entry.Id, entry.Version, entity, requestedLockMode, source);
					}
					entry.LockMode = requestedLockMode;
				}
				finally
				{
					// the database now holds a lock + the object is flushed from the cache,
					// so release the soft lock
					if (persister.HasCache)
					{
						persister.Cache.Release(ck, slock);
					}
				}
			}
		}
Example #8
0
 public override void AfterTransactionCompletion(bool success)
 {
     if (Persister.HasCache)
     {
         CacheKey ck = new CacheKey(Id, Persister.IdentifierType, Persister.RootEntityName, Session.EntityMode, Session.Factory);
         Persister.Cache.Release(ck, sLock);
     }
     if (success)
     {
         PostCommitDelete();
     }
 }
		/// <summary></summary>
		public override void AfterTransactionCompletion(bool success)
		{
			if (Persister.HasCache)
			{
				CacheKey ck = new CacheKey(
					Id,
					Persister.IdentifierType,
					(string) Persister.IdentifierSpace,
					Session.Factory
					);
				Persister.Cache.Release(ck, lck);
			}
		}
		/// <summary></summary>
		public override void AfterTransactionCompletion(bool success)
		{
			// Make 100% certain that this is called before any subsequent ScheduledUpdate.AfterTransactionCompletion()!!
			if (success && Persister.HasCache && !Persister.IsCacheInvalidationRequired)
			{
				CacheKey ck = new CacheKey(
					Id,
					Persister.IdentifierType,
					(string) Persister.IdentifierSpace,
					Session.Factory
					);
				Persister.Cache.AfterInsert(ck, cacheEntry, version);
			}
		}
		/// <summary>
		/// Do not return an item whose timestamp is later than the current
		/// transaction timestamp. (Otherwise we might compromise repeatable
		/// read unnecessarily.) Do not return an item which is soft-locked.
		/// Always go straight to the database instead.
		/// </summary>
		/// <remarks>
		/// Note that since reading an item from that cache does not actually
		/// go to the database, it is possible to see a kind of phantom read
		/// due to the underlying row being updated after we have read it
		/// from the cache. This would not be possible in a lock-based
		/// implementation of repeatable read isolation. It is also possible
		/// to overwrite changes made and committed by another transaction
		/// after the current transaction read the item from the cache. This
		/// problem would be caught by the update-time version-checking, if 
		/// the data is versioned or timestamped.
		/// </remarks>
		public object Get(CacheKey key, long txTimestamp)
		{
			lock (_lockObject)
			{
				if (log.IsDebugEnabled)
				{
					log.Debug("Cache lookup: " + key);
				}

				// commented out in H3.1
				/*try
				{
					cache.Lock( key );*/

				ILockable lockable = (ILockable) cache.Get(key);

				bool gettable = lockable != null && lockable.IsGettable(txTimestamp);

				if (gettable)
				{
					if (log.IsDebugEnabled)
					{
						log.Debug("Cache hit: " + key);
					}

					return ((CachedItem) lockable).Value;
				}
				else
				{
					if (log.IsDebugEnabled)
					{
						if (lockable == null)
						{
							log.Debug("Cache miss: " + key);
						}
						else
						{
							log.Debug("Cached item was locked: " + key);
						}
					}
					return null;
				}
				/*}
				finally
				{
					cache.Unlock( key );
				}*/
			}
		}
		/// <summary></summary>
		public override void Execute()
		{
			if (Persister.HasCache)
			{
				CacheKey ck = new CacheKey(
					Id,
					Persister.IdentifierType,
					(string) Persister.IdentifierSpace,
					Session.Factory
					);
				lck = Persister.Cache.Lock(ck, version);
			}
			Persister.Delete(Id, version, Instance, Session);
			Session.PostDelete(Instance);
		}
		public void SecondLevelCachedCollectionsFiltering()
		{
			TestData testData = new TestData(this);
			testData.Prepare();

			ISession session = OpenSession();

			// Force a collection into the second level cache, with its non-filtered elements
			Salesperson sp = (Salesperson) session.Load(typeof(Salesperson), testData.steveId);
			NHibernateUtil.Initialize(sp.Orders);
			ICollectionPersister persister = ((ISessionFactoryImplementor) sessions)
				.GetCollectionPersister(typeof(Salesperson).FullName + ".Orders");
			Assert.IsTrue(persister.HasCache, "No cache for collection");
			CacheKey cacheKey =
				new CacheKey(testData.steveId, persister.KeyType, persister.Role, EntityMode.Poco, (ISessionFactoryImplementor) sessions);
			CollectionCacheEntry cachedData = (CollectionCacheEntry)persister.Cache.Cache.Get(cacheKey);
			Assert.IsNotNull(cachedData, "collection was not in cache");

			session.Close();

			session = OpenSession();
			session.EnableFilter("fulfilledOrders").SetParameter("asOfDate", testData.lastMonth);
			sp = (Salesperson) session.CreateQuery("from Salesperson as s where s.id = :id")
			                   	.SetInt64("id", testData.steveId)
			                   	.UniqueResult();
			Assert.AreEqual(1, sp.Orders.Count, "Filtered-collection not bypassing 2L-cache");

			CollectionCacheEntry cachedData2 = (CollectionCacheEntry)persister.Cache.Cache.Get(cacheKey);
			Assert.IsNotNull(cachedData2, "collection no longer in cache!");
			Assert.AreSame(cachedData, cachedData2, "Different cache values!");

			session.Close();

			session = OpenSession();
			session.EnableFilter("fulfilledOrders").SetParameter("asOfDate", testData.lastMonth);
			sp = (Salesperson) session.Load(typeof(Salesperson), testData.steveId);
			Assert.AreEqual(1, sp.Orders.Count, "Filtered-collection not bypassing 2L-cache");

			session.Close();

			// Finally, make sure that the original cached version did not get over-written
			session = OpenSession();
			sp = (Salesperson) session.Load(typeof(Salesperson), testData.steveId);
			Assert.AreEqual(2, sp.Orders.Count, "Actual cached version got over-written");

			session.Close();
			testData.Release();
		}
		/// <summary>
		/// Get the most recent version, if available.
		/// </summary>
		public object Get(CacheKey key, long txTimestamp)
		{
			if (log.IsDebugEnabled)
			{
				log.Debug("Cache lookup: " + key);
			}

			object result = cache.Get(key);
			if (result != null)
			{
				log.Debug("Cache hit");
			}
			else
			{
				log.Debug("Cache miss");
			}
			return result;
		}
Example #15
0
        public override void AfterTransactionCompletion(bool success)
        {
            //Make 100% certain that this is called before any subsequent ScheduledUpdate.afterTransactionCompletion()!!
            IEntityPersister persister = Persister;
            if (success && IsCachePutEnabled(persister))
            {
                CacheKey ck = new CacheKey(Id, persister.IdentifierType, persister.RootEntityName, Session.EntityMode, Session.Factory);
                bool put = persister.Cache.AfterInsert(ck, cacheEntry, version);

                if (put && Session.Factory.Statistics.IsStatisticsEnabled)
                {
                    Session.Factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName);
                }
            }
            if (success)
            {
                PostCommitInsert();
            }
        }
		/// <summary></summary>
		public override void Execute()
		{
			// Don't need to lock the cache here, since if someone
			// else inserted the same pk first, the insert would fail
			Persister.Insert(Id, state, Instance, Session);

			EntityEntry entry = Session.GetEntry(Instance);
			if (entry == null)
			{
				throw new AssertionFailure("possible nonthreadsafe access to session");
			}
			entry.PostInsert();

			if (Persister.HasInsertGeneratedProperties)
			{
				Persister.ProcessInsertGeneratedProperties(Id, Instance, state, Session);
				if (Persister.IsVersionPropertyGenerated)
				{
					version = Versioning.GetVersion(state, Persister);
				}
				entry.PostUpdate(Instance, state, version);
			}


			if (Persister.HasCache && !Persister.IsCacheInvalidationRequired)
			{
				cacheEntry = new CacheEntry(Instance, Persister, Session);
				CacheKey ck = new CacheKey(
					Id,
					Persister.IdentifierType,
					(string) Persister.IdentifierSpace,
					Session.Factory
					);
				Persister.Cache.Insert(ck, cacheEntry);
			}
		}
		public virtual void OnRefresh(RefreshEvent @event, IDictionary refreshedAlready)
		{
			IEventSource source = @event.Session;

			if (source.PersistenceContext.ReassociateIfUninitializedProxy(@event.Entity))
				return;

			object obj = source.PersistenceContext.UnproxyAndReassociate(@event.Entity);

			if (refreshedAlready.Contains(obj))
			{
				log.Debug("already refreshed");
				return;
			}

			EntityEntry e = source.PersistenceContext.GetEntry(obj);
			IEntityPersister persister;
			object id;

			if (e == null)
			{
				persister = source.GetEntityPersister(null, obj); //refresh() does not pass an entityName
				id = persister.GetIdentifier(obj, source.EntityMode);
				if (log.IsDebugEnabled)
				{
					log.Debug("refreshing transient " + MessageHelper.InfoString(persister, id, source.Factory));
				}
				EntityKey key = new EntityKey(id, persister, source.EntityMode);
				if (source.PersistenceContext.GetEntry(key) != null)
				{
					throw new PersistentObjectException("attempted to refresh transient instance when persistent instance was already associated with the Session: " + 
						MessageHelper.InfoString(persister, id, source.Factory));
				}
			}
			else
			{
				if (log.IsDebugEnabled)
				{
					log.Debug("refreshing " + MessageHelper.InfoString(e.Persister, e.Id, source.Factory));
				}
				if (!e.ExistsInDatabase)
				{
					throw new HibernateException("this instance does not yet exist as a row in the database");
				}

				persister = e.Persister;
				id = e.Id;
			}

			// cascade the refresh prior to refreshing this entity
			refreshedAlready[obj] = obj;
			new Cascade(CascadingAction.Refresh, CascadePoint.BeforeRefresh, source).CascadeOn(persister, obj, refreshedAlready);

			if (e != null)
			{
				EntityKey key = new EntityKey(id, persister, source.EntityMode);
				source.PersistenceContext.RemoveEntity(key);
				if (persister.HasCollections)
					new EvictVisitor(source).Process(obj, persister);
			}

			if (persister.HasCache)
			{
				CacheKey ck = new CacheKey(id, persister.IdentifierType, persister.RootEntityName, source.EntityMode, source.Factory);
				persister.Cache.Remove(ck);
			}

			EvictCachedCollections(persister, id, source.Factory);

			// NH Different behavior : NH-1601
			// At this point the entity need the real refresh, all elementes of collections are Refreshed,
			// the collection state was evicted, but the PersistentCollection (in the entity state)
			// is associated with a possible previous session.
			new WrapVisitor(source).Process(obj, persister);

			string previousFetchProfile = source.FetchProfile;
			source.FetchProfile = "refresh";
			object result = persister.Load(id, obj, @event.LockMode, source);
			source.FetchProfile = previousFetchProfile;
			// NH Different behavior : we are ignoring transient entities without throw any kind of exception 
			// because a transient entity is "self refreshed"
			if (!ForeignKeys.IsTransient(persister.EntityName, obj, result == null, @event.Session))
				UnresolvableObjectException.ThrowIfNull(result, id, persister.EntityName);
		}
		public void EvictEntity(string entityName, object id)
		{
			IEntityPersister p = GetEntityPersister(entityName);
			if (p.HasCache)
			{
				if (log.IsDebugEnabled)
				{
					log.Debug("evicting second-level cache: " + MessageHelper.InfoString(p, id, this));
				}
				CacheKey cacheKey = new CacheKey(id, p.IdentifierType, p.RootEntityName, EntityMode.Poco, this);
				p.Cache.Remove(cacheKey);
			}
		}
		public void EvictCollection(string roleName, object id)
		{
			ICollectionPersister p = GetCollectionPersister(roleName);
			if (p.HasCache)
			{
				if (log.IsDebugEnabled)
				{
					log.Debug("evicting second-level cache: " + MessageHelper.InfoString(p, id));
				}
				CacheKey ck = new CacheKey(id, p.KeyType, p.Role, EntityMode.Poco, this);
				p.Cache.Remove(ck);
			}
		}
Example #20
0
        /// <summary> 
        /// Perform the second step of 2-phase load. Fully initialize the entity instance. 
        /// After processing a JDBC result set, we "resolve" all the associations
        /// between the entities which were instantiated and had their state
        /// "hydrated" into an array
        /// </summary>
        public static void InitializeEntity(object entity, bool readOnly, ISessionImplementor session, PreLoadEvent preLoadEvent, PostLoadEvent postLoadEvent)
        {
            //TODO: Should this be an InitializeEntityEventListener??? (watch out for performance!)
            IPersistenceContext persistenceContext = session.PersistenceContext;
            EntityEntry entityEntry = persistenceContext.GetEntry(entity);
            if (entityEntry == null)
            {
                throw new AssertionFailure("possible non-threadsafe access to the session");
            }
            IEntityPersister persister = entityEntry.Persister;
            object id = entityEntry.Id;
            object[] hydratedState = entityEntry.LoadedState;

            if (log.IsDebugEnabled)
                log.Debug("resolving associations for " + MessageHelper.InfoString(persister, id, session.Factory));

            IType[] types = persister.PropertyTypes;
            for (int i = 0; i < hydratedState.Length; i++)
            {
                object value = hydratedState[i];
                if (value != LazyPropertyInitializer.UnfetchedProperty && value != BackrefPropertyAccessor.Unknown)
                {
                    hydratedState[i] = types[i].ResolveIdentifier(value, session, entity);
                }
            }

            //Must occur after resolving identifiers!
            if (session.IsEventSource)
            {
                preLoadEvent.Entity = entity;
                preLoadEvent.State = hydratedState;
                preLoadEvent.Id = id;
                preLoadEvent.Persister=persister;
                IPreLoadEventListener[] listeners = session.Listeners.PreLoadEventListeners;
                for (int i = 0; i < listeners.Length; i++)
                {
                    listeners[i].OnPreLoad(preLoadEvent);
                }
            }

            persister.SetPropertyValues(entity, hydratedState, session.EntityMode);

            ISessionFactoryImplementor factory = session.Factory;

            if (persister.HasCache && ((session.CacheMode & CacheMode.Put) == CacheMode.Put))
            {
                if (log.IsDebugEnabled)
                    log.Debug("adding entity to second-level cache: " + MessageHelper.InfoString(persister, id, session.Factory));

                object version = Versioning.GetVersion(hydratedState, persister);
                CacheEntry entry =
                    new CacheEntry(hydratedState, persister, entityEntry.LoadedWithLazyPropertiesUnfetched, version, session, entity);
                CacheKey cacheKey = new CacheKey(id, persister.IdentifierType, persister.RootEntityName, session.EntityMode, session.Factory);
                bool put =
                    persister.Cache.Put(cacheKey, entry, session.Timestamp, version,
                                        persister.IsVersioned ? persister.VersionType.Comparator : null,
                                        UseMinimalPuts(session, entityEntry));
                //we could use persister.hasLazyProperties() instead of true

                if (put && factory.Statistics.IsStatisticsEnabled)
                {
                    factory.StatisticsImplementor.SecondLevelCachePut(persister.Cache.RegionName);
                }
            }

            if (readOnly || !persister.IsMutable)
            {
                //no need to take a snapshot - this is a
                //performance optimization, but not really
                //important, except for entities with huge
                //mutable property values
                persistenceContext.SetEntryStatus(entityEntry, Status.ReadOnly);
            }
            else
            {
                //take a snapshot
                TypeFactory.DeepCopy(hydratedState, persister.PropertyTypes, persister.PropertyUpdateability, hydratedState, session);
                persistenceContext.SetEntryStatus(entityEntry, Status.Loaded);
            }

            persister.AfterInitialize(entity, entityEntry.LoadedWithLazyPropertiesUnfetched, session);

            if (session.IsEventSource)
            {
                postLoadEvent.Entity = entity;
                postLoadEvent.Id = id;
                postLoadEvent.Persister = persister;
                IPostLoadEventListener[] listeners = session.Listeners.PostLoadEventListeners;
                for (int i = 0; i < listeners.Length; i++)
                {
                    listeners[i].OnPostLoad(postLoadEvent);
                }
            }

            if (log.IsDebugEnabled)
                log.Debug("done materializing entity " + MessageHelper.InfoString(persister, id, session.Factory));

            if (factory.Statistics.IsStatisticsEnabled)
            {
                factory.StatisticsImplementor.LoadEntity(persister.EntityName);
            }
        }
		/// <summary> Add the collection to the second-level cache </summary>
		/// <param name="lce">The entry representing the collection to add </param>
		/// <param name="persister">The persister </param>
		private void AddCollectionToCache(LoadingCollectionEntry lce, ICollectionPersister persister)
		{
			ISessionImplementor session = LoadContext.PersistenceContext.Session;
			ISessionFactoryImplementor factory = session.Factory;

			if (log.IsDebugEnabled)
			{
				log.Debug("Caching collection: " + MessageHelper.InfoString(persister, lce.Key, factory));
			}

			if (!(session.EnabledFilters.Count == 0) && persister.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 CacheKey to differentiate filtered collections from non-filtered;
				//      but CacheKey is currently used for both collections and entities; would ideally need to define two separate ones;
				//      currently this works in conjunction with the check on
				//      DefaultInitializeCollectionEventHandler.initializeCollectionFromCache() (which makes sure to not read from
				//      cache with enabled filters).
				return; // EARLY EXIT!!!!!
			}

			IComparer versionComparator;
			object version;
			if (persister.IsVersioned)
			{
				versionComparator = persister.OwnerEntityPersister.VersionType.Comparator;
				object collectionOwner = LoadContext.PersistenceContext.GetCollectionOwner(lce.Key, persister);
				version = LoadContext.PersistenceContext.GetEntry(collectionOwner).Version;
			}
			else
			{
				version = null;
				versionComparator = null;
			}

			CollectionCacheEntry entry = new CollectionCacheEntry(lce.Collection, persister);
			CacheKey cacheKey = new CacheKey(lce.Key, persister.KeyType, persister.Role, session.EntityMode, factory);
			bool put = persister.Cache.Put(cacheKey, persister.CacheEntryStructure.Structure(entry), 
			                    session.Timestamp, version, versionComparator,
													factory.Settings.IsMinimalPutsEnabled && session.CacheMode != CacheMode.Refresh);

			if (put && factory.Statistics.IsStatisticsEnabled)
			{
				factory.StatisticsImplementor.SecondLevelCachePut(persister.Cache.RegionName);
			}
		}
		/// <summary>
		/// Do nothing
		/// </summary>
		public ISoftLock Lock(CacheKey key, object version)
		{
			return null;
		}
		public void Remove(CacheKey key)
		{
			if (log.IsDebugEnabled)
			{
				log.Debug("Removing: " + key);
			}
			cache.Remove(key);
		}
        public virtual object InitializeLazyProperty(string fieldName, object entity, ISessionImplementor session)
        {
            object id = session.GetContextEntityIdentifier(entity);

            EntityEntry entry = session.PersistenceContext.GetEntry(entity);
            if (entry == null)
                throw new HibernateException("entity is not associated with the session: " + id);

            if (log.IsDebugEnabled)
            {
                log.Debug(
                    string.Format("initializing lazy properties of: {0}, field access: {1}",
                                                MessageHelper.InfoString(this, id, Factory), fieldName));
            }

            if (HasCache)
            {
                CacheKey cacheKey = new CacheKey(id, IdentifierType, EntityName, session.EntityMode, Factory);
                object ce = Cache.Get(cacheKey, session.Timestamp);
                if (ce != null)
                {
                    CacheEntry cacheEntry = (CacheEntry)CacheEntryStructure.Destructure(ce, factory);
                    if (!cacheEntry.AreLazyPropertiesUnfetched)
                    {
                        //note early exit here:
                        return InitializeLazyPropertiesFromCache(fieldName, entity, session, entry, cacheEntry);
                    }
                }
            }

            return InitializeLazyPropertiesFromDatastore(fieldName, entity, session, id, entry);
        }
		/// <summary>
		/// Do nothing
		/// </summary>
		public bool AfterInsert(CacheKey key, object value, object version)
		{
			return false;
		}
		/// <summary>
		/// Invalidate the item (again, for safety).
		/// </summary>
		public bool AfterUpdate(CacheKey key, object value, object version, ISoftLock @lock)
		{
			Release(key, @lock);
			return false;
		}
		/// <summary>
		/// Invalidate the item (again, for safety).
		/// </summary>
		public void Release(CacheKey key, ISoftLock @lock)
		{
			if (log.IsDebugEnabled)
			{
				log.Debug("Invalidating (again): " + key);
			}

			cache.Remove(key);
		}
		/// <summary>
		/// Do nothing
		/// </summary>
		public bool Insert(CacheKey key, object value, object currentVersion)
		{
			return false;
		}
        public virtual bool? IsTransient(object entity, ISessionImplementor session)
        {
            object id;
            if (CanExtractIdOutOfEntity)
            {
                id = GetIdentifier(entity, session.EntityMode);
            }
            else
            {
                id = null;
            }
            // we *always* assume an instance with a null
            // identifier or no identifier property is unsaved!
            if (id == null)
            {
                return true;
            }

            // check the version unsaved-value, if appropriate
            if (IsVersioned)
            {
                object version = GetVersion(entity, session.EntityMode);
                // let this take precedence if defined, since it works for
                // assigned identifiers
                bool? result = entityMetamodel.VersionProperty.UnsavedValue.IsUnsaved(version);
                if (result.HasValue)
                {
                    return result;
                }
            }

            // check the id unsaved-value
            bool? result2 = entityMetamodel.IdentifierProperty.UnsavedValue.IsUnsaved(id);
            if (result2.HasValue)
                return result2;

            // check to see if it is in the second-level cache
            if (HasCache)
            {
                CacheKey ck = new CacheKey(id, IdentifierType, RootEntityName, session.EntityMode, session.Factory);
                if (Cache.Get(ck, session.Timestamp) != null)
                    return false;
            }

            return null;
        }
		/// <summary>
		/// Invalidate the item
		/// </summary>
		public bool Update(CacheKey key, object value, object currentVersion, object previousVersion)
		{
			Evict(key);
			return false;
		}