⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 abstractflushingeventlistener.cs

📁 NHibernate NET开发者所需的
💻 CS
字号:
using System;
using System.Collections;
using System.Text;
using Iesi.Collections;
using log4net;
using NHibernate.Action;
using NHibernate.Collection;
using NHibernate.Engine;
using NHibernate.Impl;
using NHibernate.Persister.Entity;
using NHibernate.Util;
using Status=NHibernate.Engine.Status;

namespace NHibernate.Event.Default
{
	/// <summary>
	/// A convenience base class for listeners whose functionality results in flushing.
	/// </summary>
	[Serializable]
	public abstract class AbstractFlushingEventListener
	{
		private static readonly ILog log = LogManager.GetLogger(typeof(AbstractFlushingEventListener));

		protected virtual object Anything
		{
			get { return null; }
		}

		protected virtual CascadingAction CascadingAction
		{
			get { return CascadingAction.SaveUpdate; }
		}

		/// <summary> 
		/// Coordinates the processing necessary to get things ready for executions
		/// as db calls by preping the session caches and moving the appropriate
		/// entities and collections to their respective execution queues. 
		/// </summary>
		/// <param name="event">The flush event.</param>
		protected virtual void FlushEverythingToExecutions(FlushEvent @event)
		{
			log.Debug("flushing session");

			IEventSource session = @event.Session;
			IPersistenceContext persistenceContext = session.PersistenceContext;

			session.Interceptor.PreFlush((ICollection)persistenceContext.EntitiesByKey.Values);

			PrepareEntityFlushes(session);
			// we could move this inside if we wanted to
			// tolerate collection initializations during
			// collection dirty checking:
			PrepareCollectionFlushes(session);
			// now, any collections that are initialized
			// inside this block do not get updated - they
			// are ignored until the next flush

			persistenceContext.Flushing = true;
			try
			{
				FlushEntities(@event);
				FlushCollections(session);
			}
			finally
			{
				persistenceContext.Flushing = false;
			}

			//some statistics
			if (log.IsDebugEnabled)
			{
				StringBuilder sb = new StringBuilder(100);

				sb.Append("Flushed: ")
					.Append(session.ActionQueue.InsertionsCount).Append(" insertions, ")
					.Append(session.ActionQueue.UpdatesCount).Append(" updates, ")
					.Append(session.ActionQueue.DeletionsCount).Append(" deletions to ")
					.Append(persistenceContext.EntityEntries.Count).Append(" objects");
				log.Debug(sb.ToString());
				sb = new StringBuilder(100);
				sb.Append("Flushed: ")
					.Append(session.ActionQueue.CollectionCreationsCount).Append(" (re)creations, ")
					.Append(session.ActionQueue.CollectionUpdatesCount).Append(" updates, ")
					.Append(session.ActionQueue.CollectionRemovalsCount).Append(" removals to ")
					.Append(persistenceContext.CollectionEntries.Count).Append(" collections");

				log.Debug(sb.ToString());
				new Printer(session.Factory).ToString(persistenceContext.EntitiesByKey.Values.GetEnumerator(), session.EntityMode);
			}
		}

		private void FlushCollections(IEventSource session)
		{
			log.Debug("Processing unreferenced collections");

			ICollection list = IdentityMap.Entries(session.PersistenceContext.CollectionEntries);
			foreach (DictionaryEntry me in list)
			{
				CollectionEntry ce = (CollectionEntry)me.Value;
				if (!ce.IsReached && !ce.IsIgnore)
				{
					Collections.ProcessUnreachableCollection((IPersistentCollection)me.Key, session);
				}
			}

			// Schedule updates to collections:

			log.Debug("Scheduling collection removes/(re)creates/updates");

			list = IdentityMap.Entries(session.PersistenceContext.CollectionEntries);
			ActionQueue actionQueue = session.ActionQueue;
			foreach (DictionaryEntry me in list)
			{
				IPersistentCollection coll = (IPersistentCollection)me.Key;
				CollectionEntry ce = (CollectionEntry)me.Value;

				if (ce.IsDorecreate)
				{
					session.Interceptor.OnCollectionRecreate(coll, ce.CurrentKey);
					actionQueue.AddAction(new CollectionRecreateAction(coll, ce.CurrentPersister, ce.CurrentKey, session));
				}
				if (ce.IsDoremove)
				{
					session.Interceptor.OnCollectionRemove(coll, ce.LoadedKey);
					actionQueue.AddAction(new CollectionRemoveAction(coll, ce.LoadedPersister, ce.LoadedKey, ce.IsSnapshotEmpty(coll), session));
				}
				if (ce.IsDoupdate)
				{
					session.Interceptor.OnCollectionUpdate(coll, ce.LoadedKey);
					actionQueue.AddAction(new CollectionUpdateAction(coll, ce.LoadedPersister, ce.LoadedKey, ce.IsSnapshotEmpty(coll), session));
				}
			}
			actionQueue.SortCollectionActions();
		}

		// 1. detect any dirty entities
		// 2. schedule any entity updates
		// 3. search out any reachable collections
		private void FlushEntities(FlushEvent @event)
		{
			log.Debug("Flushing entities and processing referenced collections");

			// Among other things, updateReachables() will recursively load all
			// collections that are moving roles. This might cause entities to
			// be loaded.

			// So this needs to be safe from concurrent modification problems.
			// It is safe because of how IdentityMap implements entrySet()
			IEventSource source = @event.Session;

			ICollection list = IdentityMap.ConcurrentEntries(source.PersistenceContext.EntityEntries);
			foreach (DictionaryEntry me in list)
			{
				// Update the status of the object and if necessary, schedule an update
				EntityEntry entry = (EntityEntry)me.Value;
				Status status = entry.Status;

				if (status != Status.Loading && status != Status.Gone)
				{
					FlushEntityEvent entityEvent = new FlushEntityEvent(source, me.Key, entry);
					IFlushEntityEventListener[] listeners = source.Listeners.FlushEntityEventListeners;
					foreach (IFlushEntityEventListener listener in listeners)
						listener.OnFlushEntity(entityEvent);
				}
			}
			source.ActionQueue.SortActions();
		}

		// Initialize the flags of the CollectionEntry, including the dirty check.
		private void PrepareCollectionFlushes(ISessionImplementor session)
		{
			// Initialize dirty flags for arrays + collections with composite elements
			// and reset reached, doupdate, etc.
			log.Debug("dirty checking collections");

			ICollection list = IdentityMap.Entries(session.PersistenceContext.CollectionEntries);
			foreach (DictionaryEntry entry in list)
			{
				((CollectionEntry)entry.Value).PreFlush((IPersistentCollection)entry.Key);
			}
		}

		//process cascade save/update at the start of a flush to discover
		//any newly referenced entity that must be passed to saveOrUpdate(),
		//and also apply orphan delete
		private void PrepareEntityFlushes(IEventSource session)
		{
			log.Debug("processing flush-time cascades");

			ICollection list = IdentityMap.ConcurrentEntries(session.PersistenceContext.EntityEntries);
			//safe from concurrent modification because of how entryList() is implemented on IdentityMap
			foreach (DictionaryEntry me in list)
			{
				EntityEntry entry = (EntityEntry)me.Value;
				Status status = entry.Status;
				if (status == Status.Loaded || status == Status.Saving)
				{
					CascadeOnFlush(session, entry.Persister, me.Key, Anything);
				}
			}
		}

		private void CascadeOnFlush(IEventSource session, IEntityPersister persister, object key, object anything)
		{
			session.PersistenceContext.IncrementCascadeLevel();
			try
			{
				new Cascade(CascadingAction, CascadePoint.BeforeFlush, session).CascadeOn(persister, key, anything);
			}
			finally
			{
				session.PersistenceContext.DecrementCascadeLevel();
			}
		}

		/// <summary> 
		/// Execute all SQL and second-level cache updates, in a
		/// special order so that foreign-key constraints cannot
		/// be violated:
		/// <list type="bullet">
		/// <item> <description>Inserts, in the order they were performed</description> </item>
		/// <item> <description>Updates</description> </item>
		/// <item> <description>Deletion of collection elements</description> </item>
		/// <item> <description>Insertion of collection elements</description> </item>
		/// <item> <description>Deletes, in the order they were performed</description> </item>
		/// </list>
		/// </summary>
		protected virtual void PerformExecutions(IEventSource session)
		{
			if (log.IsDebugEnabled)
				log.Debug("executing flush");

			try
			{
				session.ConnectionManager.FlushBeginning();
				// we need to lock the collection caches before
				// executing entity inserts/updates in order to
				// account for bidi associations
				session.ActionQueue.PrepareActions();
				session.ActionQueue.ExecuteActions();
			}
			catch (HibernateException he)
			{
				if(log.IsErrorEnabled)
					log.Error("Could not synchronize database state with session", he);
				throw;
			}
			finally
			{
				session.ConnectionManager.FlushEnding();
			}
		}

		/// <summary> 
		/// 1. Recreate the collection key -> collection map
		/// 2. rebuild the collection entries
		/// 3. call Interceptor.postFlush()
		/// </summary>
		protected virtual void PostFlush(ISessionImplementor session)
		{
			if (log.IsDebugEnabled)
				log.Debug("post flush");

			IPersistenceContext persistenceContext = session.PersistenceContext;
			persistenceContext.CollectionsByKey.Clear();
			persistenceContext.BatchFetchQueue.ClearSubselects();
				//the database has changed now, so the subselect results need to be invalidated

			ISet keysToRemove = new HashedSet();
			IDictionary cEntries = persistenceContext.CollectionEntries;
			foreach (DictionaryEntry me in cEntries)
			{
				CollectionEntry collectionEntry = (CollectionEntry) me.Value;
				IPersistentCollection persistentCollection = (IPersistentCollection) me.Key;
				collectionEntry.PostFlush(persistentCollection);
				if (collectionEntry.LoadedPersister == null)
				{
					keysToRemove.Add(persistentCollection);
				}
				else
				{
					//otherwise recreate the mapping between the collection and its key
					CollectionKey collectionKey = new CollectionKey(collectionEntry.LoadedPersister, collectionEntry.LoadedKey, session.EntityMode);
					persistenceContext.CollectionsByKey[collectionKey] = persistentCollection;
				}
			}
			foreach (object key in keysToRemove)
			{
				persistenceContext.CollectionEntries.Remove(key);
			}
			session.Interceptor.PostFlush((ICollection)persistenceContext.EntitiesByKey.Values);
		}
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -