📄 querytranslator.cs
字号:
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 + -