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

📄 querytranslator.cs

📁 NHibernate NET开发者所需的
💻 CS
📖 第 1 页 / 共 4 页
字号:
using System;
using System.Collections;
using System.Data;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using Iesi.Collections;
using Iesi.Collections.Generic;
using log4net;

using NHibernate.Engine;
using NHibernate.Hql.Util;
using NHibernate.Impl;
using NHibernate.Loader;
using NHibernate.Persister.Collection;
using NHibernate.Persister.Entity;
using NHibernate.SqlCommand;
using NHibernate.Transform;
using NHibernate.Type;
using NHibernate.Util;
using NHibernate.Dialect.Function;
using System.Collections.Generic;

namespace NHibernate.Hql.Classic
{
	/// <summary> 
	/// An instance of <c>QueryTranslator</c> translates a Hibernate query string to SQL.
	/// </summary>
	public class QueryTranslator : BasicLoader, IFilterTranslator
	{
		private static readonly string[] NoReturnAliases = new string[] {};

		private readonly string queryIdentifier;
		private readonly string queryString;

		private readonly IDictionary<string, string> typeMap = new LinkedHashMap<string, string>();
		private readonly IDictionary<string, string> collections = new LinkedHashMap<string, string>();
		private IList<string> returnedTypes = new List<string>();
		private readonly IList<string> fromTypes = new List<string>();
		private readonly IList<IType> scalarTypes = new List<IType>();
		private readonly IDictionary<string, List<int>> namedParameters = new Dictionary<string, List<int>>();
		private readonly IDictionary<string, string> aliasNames = new Dictionary<string, string>();
		private readonly IDictionary<string, string> oneToOneOwnerNames = new Dictionary<string, string>();
		private readonly IDictionary<string, IAssociationType> uniqueKeyOwnerReferences = new Dictionary<string, IAssociationType>();
		private readonly IDictionary<string, IPropertyMapping> decoratedPropertyMappings = new Dictionary<string, IPropertyMapping>();

		private readonly IList<SqlString> scalarSelectTokens = new List<SqlString>(); // contains a List of strings
		private readonly IList<SqlString> whereTokens = new List<SqlString>();
		private readonly IList<SqlString> havingTokens = new List<SqlString>();
		private readonly IDictionary<string, JoinSequence> joins = new LinkedHashMap<string, JoinSequence>();
		private readonly IList<SqlString> orderByTokens = new List<SqlString>();
		private readonly IList<SqlString> groupByTokens = new List<SqlString>();
		private readonly ISet<string> querySpaces = new HashedSet<string>();
		private readonly ISet<string> entitiesToFetch = new HashedSet<string>();

		private readonly IDictionary<string, string> pathAliases = new Dictionary<string, string>();
		private readonly IDictionary<string, JoinSequence> pathJoins = new Dictionary<string, JoinSequence>();

		private IQueryable[] persisters;
		private int[] owners;
		private EntityType[] ownerAssociationTypes;
		private string[] names;
		private bool[] includeInSelect;
		private int selectLength;
		private IType[] returnTypes;
		private IType[] actualReturnTypes;
		private string[][] scalarColumnNames;
		private IDictionary<string, string> tokenReplacements;
		private int nameCount = 0;
		private int parameterCount = 0;
		private bool distinct = false;
		private bool compiled;
		private SqlString sqlString;
		private System.Type holderClass;
		private ConstructorInfo holderConstructor;
		private bool hasScalars;
		private bool shallowQuery;
		private QueryTranslator superQuery;

		private class FetchedCollections
		{
			private int count = 0;
			private List<ICollectionPersister> persisters;
			private List<string> names;
			private List<string> ownerNames;
			private List<string> suffixes;

			// True if one of persisters represents a collection that is not safe to use in multiple join scenario.
			// Currently every collection except bag is safe.
			private bool hasUnsafeCollection = false;

			private List<int> ownerColumns;

			private static string GenerateSuffix(int count)
			{
				return count + "__";
			}

			private static bool IsUnsafe(ICollectionPersister collectionPersister)
			{
				return collectionPersister.CollectionType is BagType
				       || collectionPersister.CollectionType is IdentifierBagType;
			}

			public void Add(string name, ICollectionPersister collectionPersister, string ownerName)
			{
				if (persisters == null)
				{
					persisters = new List<ICollectionPersister>(2);
					names = new List<string>(2);
					ownerNames = new List<string>(2);
					suffixes = new List<string>(2);
				}

				count++;

				hasUnsafeCollection = hasUnsafeCollection || IsUnsafe(collectionPersister);

				if (count > 1 && hasUnsafeCollection)
				{
					// The comment only mentions a bag since I don't want to confuse users.
					throw new QueryException("Cannot fetch multiple collections in a single query if one of them is a bag");
				}

				names.Add(name);
				persisters.Add(collectionPersister);
				ownerNames.Add(ownerName);
				suffixes.Add(GenerateSuffix(count - 1));
			}

			public int[] CollectionOwners
			{
				get
				{
					if (ownerColumns == null)
					{
						return null;
					}
					return ownerColumns.ToArray();
				}
			}

			public ICollectionPersister[] CollectionPersisters
			{
				get
				{
					if (persisters == null)
					{
						return null;
					}
					return persisters.ToArray();
				}
			}

			public string[] CollectionSuffixes
			{
				get
				{
					if (suffixes == null)
					{
						return null;
					}
					return suffixes.ToArray();
				}
			}

			public void AddSelectFragmentString(QuerySelect sql)
			{
				if (persisters == null)
				{
					return;
				}

				for (int i = 0; i < count; i++)
				{
					sql.AddSelectFragmentString(new SqlString(
						((IQueryableCollection) persisters[i]).SelectFragment(
							(string) names[i], (string) suffixes[i])));
				}
			}

			public void AddOrderBy(QuerySelect sql)
			{
				if (persisters == null)
				{
					return;
				}

				for (int i = 0; i < count; i++)
				{
					IQueryableCollection persister = (IQueryableCollection) persisters[i];
					if (persister.HasOrdering)
					{
						sql.AddOrderBy(persister.GetSQLOrderByString(names[i]));
					}
				}
			}

			public void InitializeCollectionOwnerColumns(IList<string> returnedTypes)
			{
				if (count == 0)
				{
					return;
				}

				ownerColumns = new List<int>(count);
				for (int i = 0; i < count; i++)
				{
					string ownerName = ownerNames[i];
					// This is quite slow in theory but there should only be a few collections
					// so it shouldn't be a problem in practice.
					int ownerIndex = returnedTypes.IndexOf(ownerName);
					ownerColumns.Add(ownerIndex);
				}
			}
		}

		private readonly FetchedCollections fetchedCollections = new FetchedCollections();

		private string[] suffixes;

		private IDictionary<string, IFilter> enabledFilters;

		private static readonly ILog log = LogManager.GetLogger(typeof(QueryTranslator));

		/// <summary> Construct a query translator </summary>
		/// <param name="queryIdentifier">
		/// A unique identifier for the query of which this
		/// translation is part; typically this is the original, user-supplied query string.
		/// </param>
		/// <param name="queryString">
		/// The "preprocessed" query string; at the very least
		/// already processed by {@link org.hibernate.hql.QuerySplitter}.
		/// </param>
		/// <param name="enabledFilters">Any enabled filters.</param>
		/// <param name="factory">The session factory. </param>
		public QueryTranslator(string queryIdentifier, string queryString, IDictionary<string, IFilter> enabledFilters, ISessionFactoryImplementor factory)
			: base(factory)
		{
			this.queryIdentifier = queryIdentifier;
			this.queryString = queryString;
			this.enabledFilters = enabledFilters;
		}

		/// <summary> 
		/// Construct a query translator
		/// </summary>
		public QueryTranslator(ISessionFactoryImplementor factory, string queryString, IDictionary<string, IFilter> enabledFilters)
			: this(queryString, queryString, enabledFilters, factory) {}

		/// <summary>
		/// Compile a subquery
		/// </summary>
		/// <param name="superquery"></param>
		protected internal void Compile(QueryTranslator superquery)
		{
			tokenReplacements = superquery.tokenReplacements;
			superQuery = superquery;
			shallowQuery = true;
			enabledFilters = superquery.EnabledFilters;

			Compile();
		}

		/// <summary>
		/// Compile a "normal" query. This method may be called multiple
		/// times. Subsequent invocations are no-ops.
		/// </summary>
		[MethodImpl(MethodImplOptions.Synchronized)]
		public void Compile(IDictionary<string,string> replacements, bool scalar)
		{
			if (!Compiled)
			{
				tokenReplacements = replacements;
				shallowQuery = scalar;

				Compile();
			}
		}

		/// <summary>
		/// Compile a filter. This method may be called multiple
		/// times. Subsequent invocations are no-ops.
		/// </summary>
		[MethodImpl(MethodImplOptions.Synchronized)]
		public void Compile(string collectionRole, IDictionary<string, string> replacements, bool scalar)
		{
			if (!Compiled)
			{
				AddFromAssociation("this", collectionRole);
				Compile(replacements, scalar);
			}
		}

		/// <summary> 
		/// Compile the query (generate the SQL).
		/// </summary>
		protected void Compile()
		{
			log.Debug("compiling query");
			try
			{
				ParserHelper.Parse(
					new PreprocessingParser(tokenReplacements),
					queryString,
					ParserHelper.HqlSeparators,
					this);
				RenderSql();
			}
			catch (QueryException qe)
			{
				qe.QueryString = queryString;
				throw;
			}
			catch (MappingException)
			{
				throw;
			}
			catch (Exception e)
			{
				log.Debug("unexpected query compilation problem", e);
				QueryException qe = new QueryException("Incorrect query syntax", e);
				qe.QueryString = queryString;
				throw qe;
			}

			PostInstantiate();

			compiled = true;
		}

		/// <summary>
		/// Persisters for the return values of a <c>List</c> style query
		/// </summary>
		/// <remarks>
		/// The <c>Persisters</c> stored by QueryTranslator have to be <see cref="IQueryable"/>.  The
		/// <c>setter</c> will attempt to cast the <c>ILoadable</c> array passed in into an 
		/// <c>IQueryable</c> array.
		/// </remarks>
		protected internal override ILoadable[] EntityPersisters
		{
			get { return persisters; }
			set { persisters = (IQueryable[]) value; }
		}

		/// <summary>
		///Types of the return values of an <c>Enumerate()</c> style query.
		///Return an array of <see cref="IType" />s.
		/// </summary>
		public virtual IType[] ReturnTypes
		{
			get { return returnTypes; }
		}

		internal virtual IType[] ActualReturnTypes
		{
			get { return actualReturnTypes; }
		}

		public virtual string[][] ScalarColumnNames
		{
			get { return scalarColumnNames; }
		}

		private static void LogQuery(string hql, string sql)
		{
			if (log.IsDebugEnabled)
			{
				log.Debug("HQL: " + hql);
				log.Debug("SQL: " + sql);
			}
		}

		internal void SetAliasName(string alias, string name)
		{
			aliasNames.Add(alias, name);
		}

		internal string GetAliasName(string alias)
		{
			string name;
			if (!aliasNames.TryGetValue(alias, out name))
			{
				if (superQuery != null)
					name = superQuery.GetAliasName(alias);
				else
					name = alias;
			}
			return name;
		}

		internal string Unalias(string path)
		{
			string alias = StringHelper.Root(path);
			string name = GetAliasName(alias);
			if (name != null)
			{
				return name + path.Substring(alias.Length);
			}
			else
			{
				return path;
			}
		}

		public void AddEntityToFetch(string name, string oneToOneOwnerName, IAssociationType ownerAssociationType)
		{
			AddEntityToFetch(name);
			if (oneToOneOwnerName != null)
			{
				oneToOneOwnerNames[name] = oneToOneOwnerName;
			}
			if (ownerAssociationType != null)
			{
				uniqueKeyOwnerReferences[name] = ownerAssociationType;
			}
		}

		public void AddEntityToFetch(string name)
		{
			entitiesToFetch.Add(name);
		}

		/// <summary></summary>
		[CLSCompliant(false)]
		// TODO: Work out why this causes an error in 1.1 - the variable sqlString is private so we're only exposing one name
			protected internal override SqlString SqlString
		{
			// this needs internal access because the WhereParser needs to be able to "get" it.
			get { return sqlString; }
			set { throw new InvalidOperationException("QueryTranslator.SqlString is read-only"); }
		}

		private int NextCount()
		{
			return (superQuery == null) ? nameCount++ : superQuery.nameCount++;
		}

⌨️ 快捷键说明

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