From ea925bb308f05dcf041fbe3ee79c159eb33663f3 Mon Sep 17 00:00:00 2001 From: Gerke Geurts Date: Wed, 24 Oct 2012 09:36:31 +0200 Subject: [PATCH 1/3] Refactored custom SQL query implementation. --- src/NHibernate/Loader/BasicLoader.cs | 1 + .../Loader/Custom/CollectionFetchReturn.cs | 27 -- .../Loader/Custom/CollectionReturn.cs | 48 -- .../Loader/Custom/ColumnCollectionAliases.cs | 53 ++- .../Loader/Custom/ColumnEntityAliases.cs | 4 +- src/NHibernate/Loader/Custom/CustomLoader.cs | 186 ++------ .../Loader/Custom/EntityFetchReturn.cs | 19 - src/NHibernate/Loader/Custom/FetchReturn.cs | 28 -- .../GeneratedCollectionAliases.cs | 46 +- src/NHibernate/Loader/Custom/IReturn.cs | 4 + .../Loader/Custom/NonScalarReturn.cs | 87 +++- src/NHibernate/Loader/Custom/RootReturn.cs | 32 -- src/NHibernate/Loader/Custom/ScalarReturn.cs | 4 +- .../Loader/Custom/Sql/SQLCustomQuery.cs | 90 ++-- .../Loader/Custom/Sql/SQLQueryContext.cs | 285 ++++++++++++ .../Loader/Custom/Sql/SQLQueryParser.cs | 87 ++-- .../Custom/Sql/SQLQueryReturnProcessor.cs | 410 ------------------ 17 files changed, 548 insertions(+), 863 deletions(-) delete mode 100644 src/NHibernate/Loader/Custom/CollectionFetchReturn.cs delete mode 100644 src/NHibernate/Loader/Custom/CollectionReturn.cs delete mode 100644 src/NHibernate/Loader/Custom/EntityFetchReturn.cs delete mode 100644 src/NHibernate/Loader/Custom/FetchReturn.cs rename src/NHibernate/Loader/{ => Custom}/GeneratedCollectionAliases.cs (83%) delete mode 100644 src/NHibernate/Loader/Custom/RootReturn.cs create mode 100644 src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs delete mode 100644 src/NHibernate/Loader/Custom/Sql/SQLQueryReturnProcessor.cs diff --git a/src/NHibernate/Loader/BasicLoader.cs b/src/NHibernate/Loader/BasicLoader.cs index 2d3ddafd877..29ac680ce44 100644 --- a/src/NHibernate/Loader/BasicLoader.cs +++ b/src/NHibernate/Loader/BasicLoader.cs @@ -1,4 +1,5 @@ using NHibernate.Engine; +using NHibernate.Loader.Custom; using NHibernate.Persister.Collection; using NHibernate.Persister.Entity; using NHibernate.Type; diff --git a/src/NHibernate/Loader/Custom/CollectionFetchReturn.cs b/src/NHibernate/Loader/Custom/CollectionFetchReturn.cs deleted file mode 100644 index da62c80955c..00000000000 --- a/src/NHibernate/Loader/Custom/CollectionFetchReturn.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace NHibernate.Loader.Custom -{ - /// Specifically a fetch return that refers to a collection association. - public class CollectionFetchReturn : FetchReturn - { - private readonly ICollectionAliases collectionAliases; - private readonly IEntityAliases elementEntityAliases; - - public CollectionFetchReturn(string alias, NonScalarReturn owner, string ownerProperty, - ICollectionAliases collectionAliases, IEntityAliases elementEntityAliases, - LockMode lockMode) : base(owner, ownerProperty, alias, lockMode) - { - this.collectionAliases = collectionAliases; - this.elementEntityAliases = elementEntityAliases; - } - - public ICollectionAliases CollectionAliases - { - get { return collectionAliases; } - } - - public IEntityAliases ElementEntityAliases - { - get { return elementEntityAliases; } - } - } -} \ No newline at end of file diff --git a/src/NHibernate/Loader/Custom/CollectionReturn.cs b/src/NHibernate/Loader/Custom/CollectionReturn.cs deleted file mode 100644 index de45101f5cf..00000000000 --- a/src/NHibernate/Loader/Custom/CollectionReturn.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace NHibernate.Loader.Custom -{ - /// - /// Represents a return which names a collection role; it - /// is used in defining a custom query for loading an entity's - /// collection in non-fetching scenarios (i.e., loading the collection - /// itself as the "root" of the result). - /// - public class CollectionReturn : NonScalarReturn - { - private readonly string ownerEntityName; - private readonly string ownerProperty; - private readonly ICollectionAliases collectionAliases; - private readonly IEntityAliases elementEntityAliases; - - public CollectionReturn(string alias, string ownerEntityName, string ownerProperty, - ICollectionAliases collectionAliases, IEntityAliases elementEntityAliases, LockMode lockMode) - : base(alias, lockMode) - { - this.ownerEntityName = ownerEntityName; - this.ownerProperty = ownerProperty; - this.collectionAliases = collectionAliases; - this.elementEntityAliases = elementEntityAliases; - } - - /// Returns the class owning the collection. - public string OwnerEntityName - { - get { return ownerEntityName; } - } - - /// Returns the name of the property representing the collection from the . - public string OwnerProperty - { - get { return ownerProperty; } - } - - public ICollectionAliases CollectionAliases - { - get { return collectionAliases; } - } - - public IEntityAliases ElementEntityAliases - { - get { return elementEntityAliases; } - } - } -} \ No newline at end of file diff --git a/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs b/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs index 22d6e406455..111a91f3835 100644 --- a/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs +++ b/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs @@ -21,13 +21,36 @@ public ColumnCollectionAliases(IDictionary userProvidedAliases { this.userProvidedAliases = userProvidedAliases; - keyAliases = GetUserProvidedAliases("key", persister.KeyColumnNames); + this.keyAliases = GetUserProvidedAliases("key", persister.KeyColumnNames); + this.indexAliases = GetUserProvidedAliases("index", persister.IndexColumnNames); + this.elementAliases = GetUserProvidedAliases("element", persister.ElementColumnNames); - indexAliases = GetUserProvidedAliases("index", persister.IndexColumnNames); + // NH-1612: Add aliases for all composite element properties to support access + // to individual composite element properties in elements. + this.elementAliases = persister.ElementType.IsComponentType + ? GetUserProvidedCompositeElementAliases(persister.ElementColumnNames) + : GetUserProvidedAliases("element", persister.ElementColumnNames); - elementAliases = GetUserProvidedAliases("element", persister.ElementColumnNames); + this.identifierAlias = GetUserProvidedAlias("id", persister.IdentifierColumnName); + } - identifierAlias = GetUserProvidedAlias("id", persister.IdentifierColumnName); + private string[] GetUserProvidedCompositeElementAliases(string[] defaultAliases) + { + if (userProvidedAliases != null) + { + var aliases = new List(); + foreach (var userProvidedAlias in userProvidedAliases) + { + if (userProvidedAlias.Key.StartsWith("element.")) + { + aliases.AddRange(userProvidedAlias.Value); + } + } + + if (aliases.Count > 0) return aliases.ToArray(); + } + + return defaultAliases; } /// @@ -96,27 +119,17 @@ private static string Join(IEnumerable aliases) private string[] GetUserProvidedAliases(string propertyPath, string[] defaultAliases) { string[] result; - if (!userProvidedAliases.TryGetValue(propertyPath, out result)) - { - return defaultAliases; - } - else - { - return result; - } + return userProvidedAliases == null || !userProvidedAliases.TryGetValue(propertyPath, out result) + ? defaultAliases + : result; } private string GetUserProvidedAlias(string propertyPath, string defaultAlias) { string[] columns; - if (!userProvidedAliases.TryGetValue(propertyPath, out columns)) - { - return defaultAlias; - } - else - { - return columns[0]; - } + return userProvidedAliases == null || !userProvidedAliases.TryGetValue(propertyPath, out columns) + ? defaultAlias + : columns[0]; } } } diff --git a/src/NHibernate/Loader/Custom/ColumnEntityAliases.cs b/src/NHibernate/Loader/Custom/ColumnEntityAliases.cs index bfcf2cb2096..5a4ba94b7b9 100644 --- a/src/NHibernate/Loader/Custom/ColumnEntityAliases.cs +++ b/src/NHibernate/Loader/Custom/ColumnEntityAliases.cs @@ -8,8 +8,8 @@ namespace NHibernate.Loader.Custom /// public class ColumnEntityAliases : DefaultEntityAliases { - public ColumnEntityAliases(IDictionary returnProperties, ILoadable persister, string suffix) - : base(returnProperties, persister, suffix) {} + public ColumnEntityAliases(IDictionary returnProperties, ILoadable persister) + : base(returnProperties, persister, null) {} protected override string[] GetIdentifierAliases(ILoadable persister, string suffix) { diff --git a/src/NHibernate/Loader/Custom/CustomLoader.cs b/src/NHibernate/Loader/Custom/CustomLoader.cs index 73dadab5f0a..d12e79ed0db 100644 --- a/src/NHibernate/Loader/Custom/CustomLoader.cs +++ b/src/NHibernate/Loader/Custom/CustomLoader.cs @@ -25,13 +25,13 @@ public partial class CustomLoader : Loader private readonly SqlString sql; private readonly ISet querySpaces = new HashSet(); - private List parametersSpecifications; + private readonly List parametersSpecifications; - private readonly IQueryable[] entityPersisters; + private readonly ILoadable[] entityPersisters; private readonly int[] entityOwners; private readonly IEntityAliases[] entityAliases; - private readonly IQueryableCollection[] collectionPersisters; + private readonly ICollectionPersister[] collectionPersisters; private readonly int[] collectionOwners; private readonly ICollectionAliases[] collectionAliases; @@ -48,19 +48,18 @@ public CustomLoader(ICustomQuery customQuery, ISessionFactoryImplementor factory querySpaces.UnionWith(customQuery.QuerySpaces); parametersSpecifications = customQuery.CollectedParametersSpecifications.ToList(); - List entitypersisters = new List(); + List entitypersisters = new List(); List entityowners = new List(); List entityaliases = new List(); - List collectionpersisters = new List(); + List collectionpersisters = new List(); List collectionowners = new List(); List collectionaliases = new List(); List lockmodes = new List(); List resultColumnProcessors = new List(); - List nonScalarReturnList = new List(); List resulttypes = new List(); - List specifiedAliases = new List(); + List transformeraliases = new List(); List includeInResultRowList = new List(); @@ -69,106 +68,56 @@ public CustomLoader(ICustomQuery customQuery, ISessionFactoryImplementor factory foreach (IReturn rtn in customQuery.CustomQueryReturns) { + transformeraliases.Add(rtn.Alias); + if (rtn is ScalarReturn) { - ScalarReturn scalarRtn = (ScalarReturn) rtn; - resulttypes.Add(scalarRtn.Type); - specifiedAliases.Add(scalarRtn.ColumnAlias); - resultColumnProcessors.Add(new ScalarResultColumnProcessor(scalarRtn.ColumnAlias, scalarRtn.Type)); + resulttypes.Add(rtn.Type); + resultColumnProcessors.Add(new ScalarResultColumnProcessor(rtn.Alias, rtn.Type)); includeInResultRowList.Add(true); hasScalars = true; + continue; } - else if (rtn is RootReturn) - { - RootReturn rootRtn = (RootReturn) rtn; - IQueryable persister = (IQueryable) factory.GetEntityPersister(rootRtn.EntityName); - entitypersisters.Add(persister); - lockmodes.Add(rootRtn.LockMode); - resultColumnProcessors.Add(new NonScalarResultColumnProcessor(returnableCounter++)); - nonScalarReturnList.Add(rtn); - entityowners.Add(-1); - resulttypes.Add(persister.Type); - specifiedAliases.Add(rootRtn.Alias); - entityaliases.Add(rootRtn.EntityAliases); - querySpaces.UnionWith(persister.QuerySpaces); - includeInResultRowList.Add(true); - } - else if (rtn is CollectionReturn) + + var nonScalarRtn = rtn as NonScalarReturn; + if (nonScalarRtn != null) { - CollectionReturn collRtn = (CollectionReturn) rtn; - string role = collRtn.OwnerEntityName + "." + collRtn.OwnerProperty; - IQueryableCollection persister = (IQueryableCollection) factory.GetCollectionPersister(role); - collectionpersisters.Add(persister); - lockmodes.Add(collRtn.LockMode); - resultColumnProcessors.Add(new NonScalarResultColumnProcessor(returnableCounter++)); - nonScalarReturnList.Add(rtn); - collectionowners.Add(-1); - resulttypes.Add(persister.Type); - specifiedAliases.Add(collRtn.Alias); - collectionaliases.Add(collRtn.CollectionAliases); - // determine if the collection elements are entities... - IType elementType = persister.ElementType; - if (elementType.IsEntityType) + lockmodes.Add(nonScalarRtn.LockMode); + + var ownerIndex = nonScalarRtn.Owner != null + ? entitypersisters.IndexOf(nonScalarRtn.Owner.EntityPersister) + : -1; + if (nonScalarRtn.EntityPersister != null) { - IQueryable elementPersister = (IQueryable) ((EntityType) elementType).GetAssociatedJoinable(factory); - entitypersisters.Add(elementPersister); - entityowners.Add(-1); - entityaliases.Add(collRtn.ElementEntityAliases); - querySpaces.UnionWith(elementPersister.QuerySpaces); + entitypersisters.Add(nonScalarRtn.EntityPersister); + entityaliases.Add(nonScalarRtn.EntityAliases); + entityowners.Add(ownerIndex); + querySpaces.UnionWith(nonScalarRtn.EntityPersister.QuerySpaces); } - includeInResultRowList.Add(true); - } - else if (rtn is EntityFetchReturn) - { - EntityFetchReturn fetchRtn = (EntityFetchReturn) rtn; - NonScalarReturn ownerDescriptor = fetchRtn.Owner; - int ownerIndex = nonScalarReturnList.IndexOf(ownerDescriptor); - entityowners.Add(ownerIndex); - lockmodes.Add(fetchRtn.LockMode); - IQueryable ownerPersister = DetermineAppropriateOwnerPersister(ownerDescriptor); - EntityType fetchedType = (EntityType) ownerPersister.GetPropertyType(fetchRtn.OwnerProperty); - string entityName = fetchedType.GetAssociatedEntityName(Factory); - IQueryable persister = (IQueryable) factory.GetEntityPersister(entityName); - entitypersisters.Add(persister); - nonScalarReturnList.Add(rtn); - specifiedAliases.Add(fetchRtn.Alias); - entityaliases.Add(fetchRtn.EntityAliases); - querySpaces.UnionWith(persister.QuerySpaces); - includeInResultRowList.Add(false); - } - else if (rtn is CollectionFetchReturn) - { - CollectionFetchReturn fetchRtn = (CollectionFetchReturn) rtn; - NonScalarReturn ownerDescriptor = fetchRtn.Owner; - int ownerIndex = nonScalarReturnList.IndexOf(ownerDescriptor); - collectionowners.Add(ownerIndex); - lockmodes.Add(fetchRtn.LockMode); - IQueryable ownerPersister = DetermineAppropriateOwnerPersister(ownerDescriptor); - string role = ownerPersister.EntityName + '.' + fetchRtn.OwnerProperty; - IQueryableCollection persister = (IQueryableCollection) factory.GetCollectionPersister(role); - collectionpersisters.Add(persister); - nonScalarReturnList.Add(rtn); - specifiedAliases.Add(fetchRtn.Alias); - collectionaliases.Add(fetchRtn.CollectionAliases); - // determine if the collection elements are entities... - IType elementType = persister.ElementType; - if (elementType.IsEntityType) + if (nonScalarRtn.CollectionPersister != null) { - IQueryable elementPersister = (IQueryable) ((EntityType) elementType).GetAssociatedJoinable(factory); - entitypersisters.Add(elementPersister); - entityowners.Add(ownerIndex); - entityaliases.Add(fetchRtn.ElementEntityAliases); - querySpaces.UnionWith(elementPersister.QuerySpaces); + collectionpersisters.Add(nonScalarRtn.CollectionPersister); + collectionaliases.Add(nonScalarRtn.CollectionAliases); + collectionowners.Add(ownerIndex); } - includeInResultRowList.Add(false); - } - else - { - throw new HibernateException("unexpected custom query return type : " + rtn.GetType().FullName); + if (nonScalarRtn.Owner == null) + { + resulttypes.Add(nonScalarRtn.Type); + resultColumnProcessors.Add(new NonScalarResultColumnProcessor(returnableCounter++)); + includeInResultRowList.Add(true); + } + else + { + includeInResultRowList.Add(false); + } + + continue; } + + throw new HibernateException("unexpected custom query return type : " + rtn.GetType().FullName); } - entityPersisters = entitypersisters.ToArray(); + entityPersisters = entitypersisters.Cast().ToArray(); entityOwners = entityowners.ToArray(); entityAliases = entityaliases.ToArray(); collectionPersisters = collectionpersisters.ToArray(); @@ -176,7 +125,7 @@ public CustomLoader(ICustomQuery customQuery, ISessionFactoryImplementor factory collectionAliases = collectionaliases.ToArray(); lockModes = lockmodes.ToArray(); ResultTypes = resulttypes.ToArray(); - transformerAliases = specifiedAliases.ToArray(); + transformerAliases = transformeraliases.ToArray(); rowProcessor = new ResultRowProcessor(hasScalars, resultColumnProcessors.ToArray()); includeInResultRow = includeInResultRowList.ToArray(); } @@ -191,6 +140,10 @@ protected override int[] CollectionOwners get { return collectionOwners; } } + /// + /// An array of indexes of the entity that owns a one-to-one association + /// to the entity at the given index (-1 if there is no "owner") + /// protected override int[] Owners { get { return entityOwners; } @@ -211,49 +164,6 @@ protected override ICollectionAliases[] CollectionAliases get { return collectionAliases; } } - private IQueryable DetermineAppropriateOwnerPersister(NonScalarReturn ownerDescriptor) - { - string entityName = null; - RootReturn odrr = ownerDescriptor as RootReturn; - if (odrr != null) - { - entityName = odrr.EntityName; - } - else if (ownerDescriptor is CollectionReturn) - { - CollectionReturn collRtn = (CollectionReturn) ownerDescriptor; - string role = collRtn.OwnerEntityName + "." + collRtn.OwnerProperty; - ICollectionPersister persister = Factory.GetCollectionPersister(role); - EntityType ownerType = (EntityType) persister.ElementType; - entityName = ownerType.GetAssociatedEntityName(Factory); - } - else if (ownerDescriptor is FetchReturn) - { - FetchReturn fetchRtn = (FetchReturn) ownerDescriptor; - IQueryable persister = DetermineAppropriateOwnerPersister(fetchRtn.Owner); - IType ownerType = persister.GetPropertyType(fetchRtn.OwnerProperty); - if (ownerType.IsEntityType) - { - entityName = ((EntityType) ownerType).GetAssociatedEntityName(Factory); - } - else if (ownerType.IsCollectionType) - { - IType ownerCollectionElementType = ((CollectionType) ownerType).GetElementType(Factory); - if (ownerCollectionElementType.IsEntityType) - { - entityName = ((EntityType) ownerCollectionElementType).GetAssociatedEntityName(Factory); - } - } - } - - if (entityName == null) - { - throw new HibernateException("Could not determine fetch owner : " + ownerDescriptor); - } - - return (IQueryable) Factory.GetEntityPersister(entityName); - } - public override string QueryIdentifier { get { return sql.ToString(); } diff --git a/src/NHibernate/Loader/Custom/EntityFetchReturn.cs b/src/NHibernate/Loader/Custom/EntityFetchReturn.cs deleted file mode 100644 index 516fd5b347b..00000000000 --- a/src/NHibernate/Loader/Custom/EntityFetchReturn.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace NHibernate.Loader.Custom -{ - /// Specifically a fetch return that refers to an entity association. - public class EntityFetchReturn : FetchReturn - { - private readonly IEntityAliases entityAliases; - - public EntityFetchReturn(string alias, IEntityAliases entityAliases, NonScalarReturn owner, string ownerProperty, - LockMode lockMode) : base(owner, ownerProperty, alias, lockMode) - { - this.entityAliases = entityAliases; - } - - public IEntityAliases EntityAliases - { - get { return entityAliases; } - } - } -} diff --git a/src/NHibernate/Loader/Custom/FetchReturn.cs b/src/NHibernate/Loader/Custom/FetchReturn.cs deleted file mode 100644 index dc92f040d26..00000000000 --- a/src/NHibernate/Loader/Custom/FetchReturn.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace NHibernate.Loader.Custom -{ - /// Represents a return which names a fetched association. - public abstract class FetchReturn : NonScalarReturn - { - private readonly NonScalarReturn owner; - private readonly string ownerProperty; - - public FetchReturn(NonScalarReturn owner, string ownerProperty, string alias, LockMode lockMode) - : base(alias, lockMode) - { - this.owner = owner; - this.ownerProperty = ownerProperty; - } - - /// Retrieves the return descriptor for the owner of this fetch. - public NonScalarReturn Owner - { - get { return owner; } - } - - /// The name of the property on the owner which represents this association. - public string OwnerProperty - { - get { return ownerProperty; } - } - } -} \ No newline at end of file diff --git a/src/NHibernate/Loader/GeneratedCollectionAliases.cs b/src/NHibernate/Loader/Custom/GeneratedCollectionAliases.cs similarity index 83% rename from src/NHibernate/Loader/GeneratedCollectionAliases.cs rename to src/NHibernate/Loader/Custom/GeneratedCollectionAliases.cs index dfed501a48c..26161d2977c 100644 --- a/src/NHibernate/Loader/GeneratedCollectionAliases.cs +++ b/src/NHibernate/Loader/Custom/GeneratedCollectionAliases.cs @@ -3,7 +3,7 @@ using NHibernate.Persister.Collection; using NHibernate.Util; -namespace NHibernate.Loader +namespace NHibernate.Loader.Custom { /// /// CollectionAliases which handles the logic of selecting user provided aliases (via return-property), @@ -42,16 +42,21 @@ public GeneratedCollectionAliases(ICollectionPersister persister, string str) private string[] GetUserProvidedCompositeElementAliases(string[] defaultAliases) { - var aliases = new List(); - foreach (KeyValuePair userProvidedAlias in userProvidedAliases) + if (userProvidedAliases != null) { - if (userProvidedAlias.Key.StartsWith("element.", StringComparison.Ordinal)) + var aliases = new List(); + foreach (var userProvidedAlias in userProvidedAliases) { - aliases.AddRange(userProvidedAlias.Value); + if (userProvidedAlias.Key.StartsWith("element.", StringComparison.Ordinal)) + { + aliases.AddRange(userProvidedAlias.Value); + } } + + if (aliases.Count > 0) return aliases.ToArray(); } - return aliases.Count > 0 ? aliases.ToArray() : defaultAliases; + return defaultAliases; } /// @@ -105,38 +110,23 @@ public override string ToString() private static string Join(IEnumerable aliases) { - if (aliases == null) - { - return null; - } - - return StringHelper.Join(", ", aliases); + return aliases == null ? null : StringHelper.Join(", ", aliases); } private string[] GetUserProvidedAliases(string propertyPath, string[] defaultAliases) { string[] result; - if (!userProvidedAliases.TryGetValue(propertyPath, out result)) - { - return defaultAliases; - } - else - { - return result; - } + return userProvidedAliases == null || !userProvidedAliases.TryGetValue(propertyPath, out result) + ? defaultAliases + : result; } private string GetUserProvidedAlias(string propertyPath, string defaultAlias) { string[] columns; - if (!userProvidedAliases.TryGetValue(propertyPath, out columns)) - { - return defaultAlias; - } - else - { - return columns[0]; - } + return userProvidedAliases == null || !userProvidedAliases.TryGetValue(propertyPath, out columns) + ? defaultAlias + : columns[0]; } } } diff --git a/src/NHibernate/Loader/Custom/IReturn.cs b/src/NHibernate/Loader/Custom/IReturn.cs index 5012044b696..1d13f8910e5 100644 --- a/src/NHibernate/Loader/Custom/IReturn.cs +++ b/src/NHibernate/Loader/Custom/IReturn.cs @@ -1,7 +1,11 @@ +using NHibernate.Type; + namespace NHibernate.Loader.Custom { /// Represents a return in a custom query. public interface IReturn { + string Alias { get; } + IType Type { get; } } } \ No newline at end of file diff --git a/src/NHibernate/Loader/Custom/NonScalarReturn.cs b/src/NHibernate/Loader/Custom/NonScalarReturn.cs index 1776cbc8fca..6dcca5b3941 100644 --- a/src/NHibernate/Loader/Custom/NonScalarReturn.cs +++ b/src/NHibernate/Loader/Custom/NonScalarReturn.cs @@ -1,29 +1,106 @@ +using System; +using NHibernate.Loader.Custom.Sql; +using NHibernate.Persister.Collection; +using NHibernate.Persister.Entity; +using NHibernate.Type; + namespace NHibernate.Loader.Custom { /// Represents some non-scalar (entity/collection) return within the query result. - public abstract class NonScalarReturn : IReturn + internal class NonScalarReturn : IReturn { private readonly string alias; private readonly LockMode lockMode; + private readonly NonScalarReturn owner; + private readonly ISqlLoadable entityPersister; + private readonly IEntityAliases entityAliases; + private readonly ISqlLoadableCollection collectionPersister; + private readonly ICollectionAliases collectionAliases; + + public NonScalarReturn(SQLQueryContext context, bool queryHasAliases, string alias, LockMode lockMode) + : this(context, queryHasAliases, alias, lockMode, null) + {} - public NonScalarReturn(string alias, LockMode lockMode) + public NonScalarReturn(SQLQueryContext context, bool queryHasAliases, string alias, LockMode lockMode, NonScalarReturn owner) { - this.alias = alias; + if (context == null) + { + throw new ArgumentNullException("context"); + } if (string.IsNullOrEmpty(alias)) { throw new HibernateException("alias must be specified"); } + + this.alias = alias; this.lockMode = lockMode; + this.owner = owner; + + this.collectionPersister = context.GetCollectionPersister(alias); + if (this.collectionPersister != null) + { + var collectionPropertyResultMap = context.GetCollectionPropertyResultsMap(alias); + this.collectionAliases = queryHasAliases + ? new GeneratedCollectionAliases(collectionPropertyResultMap, this.collectionPersister, context.GetCollectionSuffix(alias)) + : (ICollectionAliases)new ColumnCollectionAliases(collectionPropertyResultMap, this.collectionPersister); + } + + if (this.collectionPersister == null || this.CollectionPersister.ElementType.IsEntityType) + { + this.entityPersister = context.GetEntityPersister(alias); + if (this.entityPersister != null) + { + var entityPropertyResultMap = context.GetEntityPropertyResultsMap(alias); + this.entityAliases = queryHasAliases + ? new DefaultEntityAliases(entityPropertyResultMap, this.entityPersister, context.GetEntitySuffix(alias)) + : new ColumnEntityAliases(entityPropertyResultMap, this.entityPersister); + } + } } public string Alias { - get { return alias; } + get { return this.alias; } } public LockMode LockMode { - get { return lockMode; } + get { return this.lockMode; } + } + + public NonScalarReturn Owner + { + get { return this.owner; } + } + + public ICollectionPersister CollectionPersister + { + get { return this.collectionPersister; } + } + + public ICollectionAliases CollectionAliases + { + get { return this.collectionAliases; } + } + + public ISqlLoadable EntityPersister + { + get { return this.entityPersister; } + } + + public IEntityAliases EntityAliases + { + get { return this.entityAliases; } + } + + public IType Type + { + get + { + if (this.collectionPersister != null) return this.collectionPersister.CollectionType; + if (this.entityPersister != null) return this.entityPersister.EntityMetamodel.EntityType; + return null; + } } } } \ No newline at end of file diff --git a/src/NHibernate/Loader/Custom/RootReturn.cs b/src/NHibernate/Loader/Custom/RootReturn.cs deleted file mode 100644 index 4337934a56d..00000000000 --- a/src/NHibernate/Loader/Custom/RootReturn.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace NHibernate.Loader.Custom -{ - /// - /// Represents a return which names a "root" entity. - /// - /// - /// A root entity means it is explicitly a "column" in the result, as opposed to - /// a fetched association. - /// - public class RootReturn : NonScalarReturn - { - private readonly string entityName; - private readonly IEntityAliases entityAliases; - - public RootReturn(string alias, string entityName, IEntityAliases entityAliases, LockMode lockMode) - : base(alias, lockMode) - { - this.entityName = entityName; - this.entityAliases = entityAliases; - } - - public string EntityName - { - get { return entityName; } - } - - public IEntityAliases EntityAliases - { - get { return entityAliases; } - } - } -} \ No newline at end of file diff --git a/src/NHibernate/Loader/Custom/ScalarReturn.cs b/src/NHibernate/Loader/Custom/ScalarReturn.cs index 5193d4fbbd0..9d30538d2f2 100644 --- a/src/NHibernate/Loader/Custom/ScalarReturn.cs +++ b/src/NHibernate/Loader/Custom/ScalarReturn.cs @@ -3,7 +3,7 @@ namespace NHibernate.Loader.Custom { /// Represent a scalar (AKA simple value) return within a query result. - public class ScalarReturn : IReturn + internal class ScalarReturn : IReturn { private readonly IType type; private readonly string columnAlias; @@ -19,7 +19,7 @@ public IType Type get { return type; } } - public string ColumnAlias + public string Alias { get { return columnAlias; } } diff --git a/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs b/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs index 57ca5afb81c..a5c101c24cf 100644 --- a/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs +++ b/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs @@ -4,10 +4,7 @@ using NHibernate.Engine; using NHibernate.Engine.Query.Sql; using NHibernate.Param; -using NHibernate.Persister.Collection; -using NHibernate.Persister.Entity; using NHibernate.SqlCommand; -using NHibernate.Util; namespace NHibernate.Loader.Custom.Sql { @@ -17,22 +14,21 @@ public class SQLCustomQuery : ICustomQuery { private static readonly INHibernateLogger log = NHibernateLogger.For(typeof (SQLCustomQuery)); - private readonly List customQueryReturns = new List(); + private readonly List customQueryReturns; private readonly ISet querySpaces = new HashSet(); private readonly SqlString sql; private readonly List parametersSpecifications; public SQLCustomQuery(INativeSQLQueryReturn[] queryReturns, string sqlQuery, ICollection additionalQuerySpaces, - ISessionFactoryImplementor factory) + ISessionFactoryImplementor factory) { log.Debug("starting processing of sql query [{0}]", sqlQuery); - SQLQueryReturnProcessor processor = new SQLQueryReturnProcessor(queryReturns, factory); - SQLQueryReturnProcessor.ResultAliasContext aliasContext = processor.Process(); + var processor = new SQLQueryContext(queryReturns, factory); - SQLQueryParser parser = new SQLQueryParser(factory, sqlQuery, new ParserContext(aliasContext)); - sql = parser.Process(); - ArrayHelper.AddAll(customQueryReturns, processor.GenerateCustomReturns(parser.QueryHasAliases)); - parametersSpecifications = parser.CollectedParametersSpecifications.ToList(); + var parser = new SQLQueryParser(factory, sqlQuery, processor); + this.sql = parser.Process(); + this.customQueryReturns = GenerateCustomReturns(queryReturns, parser.QueryHasAliases, processor).ToList(); + this.parametersSpecifications = parser.CollectedParametersSpecifications.ToList(); if (additionalQuerySpaces != null) { @@ -64,53 +60,39 @@ public IList CustomQueryReturns #endregion - private class ParserContext : SQLQueryParser.IParserContext - { - private readonly SQLQueryReturnProcessor.ResultAliasContext aliasContext; - - public ParserContext(SQLQueryReturnProcessor.ResultAliasContext aliasContext) - { - this.aliasContext = aliasContext; - } - - #region IParserContext Members - - public bool IsEntityAlias(string alias) - { - return GetEntityPersisterByAlias(alias) != null; - } - - public ISqlLoadable GetEntityPersisterByAlias(string alias) - { - return aliasContext.GetEntityPersister(alias); - } - - public string GetEntitySuffixByAlias(string alias) - { - return aliasContext.GetEntitySuffix(alias); - } - - public bool IsCollectionAlias(string alias) - { - return GetCollectionPersisterByAlias(alias) != null; - } - - public ISqlLoadableCollection GetCollectionPersisterByAlias(string alias) - { - return aliasContext.GetCollectionPersister(alias); - } + #region private methods - public string GetCollectionSuffixByAlias(string alias) - { - return aliasContext.GetCollectionSuffix(alias); - } + private static IEnumerable GenerateCustomReturns(IEnumerable queryReturns, bool queryHadAliases, SQLQueryContext context) + { + IDictionary customReturnsByAlias = new Dictionary(); - public IDictionary GetPropertyResultsMapByAlias(string alias) + foreach (var nativeRtn in queryReturns) { - return aliasContext.GetPropertyResultsMap(alias); + var nativeScalarRtn = nativeRtn as NativeSQLQueryScalarReturn; + if (nativeScalarRtn != null) + { + yield return new ScalarReturn(nativeScalarRtn.Type, nativeScalarRtn.ColumnAlias); + continue; + } + + var nativeJoinRtn = nativeRtn as NativeSQLQueryJoinReturn; + if (nativeJoinRtn != null) + { + var owner = customReturnsByAlias[nativeJoinRtn.OwnerAlias]; + var fetchReturn = new NonScalarReturn(context, queryHadAliases, nativeJoinRtn.Alias, nativeJoinRtn.LockMode, owner); + yield return customReturnsByAlias[fetchReturn.Alias] = fetchReturn; + continue; + } + + var nativeNonScalarRtn = nativeRtn as NativeSQLQueryNonScalarReturn; + if (nativeNonScalarRtn != null) + { + var nonFetchReturn = new NonScalarReturn(context, queryHadAliases, nativeNonScalarRtn.Alias, nativeNonScalarRtn.LockMode); + yield return customReturnsByAlias[nonFetchReturn.Alias] = nonFetchReturn; + } } - - #endregion } + + #endregion } } diff --git a/src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs b/src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs new file mode 100644 index 00000000000..fa682449b36 --- /dev/null +++ b/src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs @@ -0,0 +1,285 @@ +using System.Collections.Generic; +using NHibernate.Engine; +using NHibernate.Engine.Query.Sql; +using NHibernate.Persister.Collection; +using NHibernate.Persister.Entity; +using NHibernate.Type; + +namespace NHibernate.Loader.Custom.Sql +{ + /// + /// Provides mappings from entity and collection aliases to persisters, suffixes + /// and custom property result maps. + /// + internal class SQLQueryContext + { + private static readonly INHibernateLogger log = NHibernateLogger.For(typeof (SQLQueryContext)); + + #region Instance fields + + private readonly Dictionary alias2Return = + new Dictionary(); + + private readonly Dictionary alias2OwnerAlias = new Dictionary(); + + private readonly Dictionary alias2EntityPersister = new Dictionary(); + private readonly Dictionary alias2EntitySuffix = new Dictionary(); + private readonly Dictionary> entityPropertyResultMaps = + new Dictionary>(); + + private readonly Dictionary alias2CollectionPersister = + new Dictionary(); + private readonly Dictionary alias2CollectionSuffix = new Dictionary(); + private readonly Dictionary> collectionPropertyResultMaps = + new Dictionary>(); + + private readonly ISessionFactoryImplementor factory; + + private int entitySuffixSeed; + private int collectionSuffixSeed; + + #endregion + + #region .ctor + + public SQLQueryContext(INativeSQLQueryReturn[] queryReturns, ISessionFactoryImplementor factory) + { + this.factory = factory; + + // first, break down the returns into maps keyed by alias + // so that role returns can be more easily resolved to their owners + foreach (INativeSQLQueryReturn rtn in queryReturns) + { + var nonScalarRtn = rtn as NativeSQLQueryNonScalarReturn; + if (nonScalarRtn != null) + { + alias2Return[nonScalarRtn.Alias] = rtn; + var joinRtn = rtn as NativeSQLQueryJoinReturn; + if (joinRtn != null) + { + alias2OwnerAlias[joinRtn.Alias] = joinRtn.OwnerAlias; + } + } + } + + // Now, process the returns + foreach (INativeSQLQueryReturn rtn in queryReturns) + { + ProcessReturn(rtn); + } + } + + #endregion + + #region ISQLQueryAliasContext implementation + + public bool IsEntityAlias(string alias) + { + return alias2EntityPersister.ContainsKey(alias); + } + + public ISqlLoadable GetEntityPersister(string alias) + { + ISqlLoadable result; + alias2EntityPersister.TryGetValue(alias, out result); + return result; + } + + public string GetEntitySuffix(string alias) + { + string result; + alias2EntitySuffix.TryGetValue(alias, out result); + return result; + } + + public IDictionary GetEntityPropertyResultsMap(string alias) + { + IDictionary result; + entityPropertyResultMaps.TryGetValue(alias, out result); + return result; + } + + public bool IsCollectionAlias(string alias) + { + return alias2CollectionPersister.ContainsKey(alias); + } + + public ISqlLoadableCollection GetCollectionPersister(string alias) + { + ISqlLoadableCollection result; + alias2CollectionPersister.TryGetValue(alias, out result); + return result; + } + + public string GetCollectionSuffix(string alias) + { + string result; + alias2CollectionSuffix.TryGetValue(alias, out result); + return result; + } + + public IDictionary GetCollectionPropertyResultsMap(string alias) + { + IDictionary result; + collectionPropertyResultMaps.TryGetValue(alias, out result); + return result; + } + + #endregion + + #region Private methods + + private ISqlLoadable GetSQLLoadable(string entityName) + { + IEntityPersister persister = factory.GetEntityPersister(entityName); + var persisterAsSqlLoadable = persister as ISqlLoadable; + if (persisterAsSqlLoadable == null) + { + throw new MappingException("class persister is not ISqlLoadable: " + entityName); + } + return persisterAsSqlLoadable; + } + + private string GenerateEntitySuffix() + { + return BasicLoader.GenerateSuffixes(entitySuffixSeed++, 1)[0]; + } + + private string GenerateCollectionSuffix() + { + return collectionSuffixSeed++ + "__"; + } + + private void ProcessReturn(INativeSQLQueryReturn rtn) + { + if (rtn is NativeSQLQueryScalarReturn) + { + ProcessScalarReturn((NativeSQLQueryScalarReturn) rtn); + } + else if (rtn is NativeSQLQueryRootReturn) + { + ProcessRootReturn((NativeSQLQueryRootReturn) rtn); + } + else if (rtn is NativeSQLQueryCollectionReturn) + { + ProcessCollectionReturn((NativeSQLQueryCollectionReturn) rtn); + } + else + { + ProcessJoinReturn((NativeSQLQueryJoinReturn) rtn); + } + } + + private void ProcessScalarReturn(NativeSQLQueryScalarReturn typeReturn) {} + + private void ProcessRootReturn(NativeSQLQueryRootReturn rootReturn) + { + if (alias2EntityPersister.ContainsKey(rootReturn.Alias)) + { + // already been processed... + return; + } + + ISqlLoadable persister = GetSQLLoadable(rootReturn.ReturnEntityName); + AddPersister(rootReturn.Alias, rootReturn.PropertyResultsMap, persister); + } + + private void AddPersister(string alias, IDictionary propertyResultMap, ISqlLoadable persister) + { + alias2EntityPersister[alias] = persister; + string suffix = GenerateEntitySuffix(); + log.Debug("mapping alias [" + alias + "] to entity-suffix [" + suffix + "]"); + alias2EntitySuffix[alias] = suffix; + if (propertyResultMap != null && propertyResultMap.Count > 0) + { + entityPropertyResultMaps[alias] = propertyResultMap; + } + } + + private void AddCollection(string role, string alias, IDictionary propertyResultMap) + { + ISqlLoadableCollection collectionPersister = (ISqlLoadableCollection) factory.GetCollectionPersister(role); + alias2CollectionPersister[alias] = collectionPersister; + string suffix = GenerateCollectionSuffix(); + log.Debug("mapping alias [" + alias + "] to collection-suffix [" + suffix + "]"); + alias2CollectionSuffix[alias] = suffix; + if (propertyResultMap != null && propertyResultMap.Count > 0) + { + collectionPropertyResultMaps[alias] = propertyResultMap; + } + + if (collectionPersister.IsOneToMany) + { + var persister = (ISqlLoadable)collectionPersister.ElementPersister; + AddPersister(alias, FilterElementProperties(propertyResultMap), persister); + } + } + + private IDictionary FilterElementProperties(IDictionary propertyResults) + { + const string PREFIX = "element."; + + var result = new Dictionary(propertyResults.Count); + foreach (KeyValuePair element in propertyResults) + { + string path = element.Key; + if (path.StartsWith(PREFIX)) + { + result[path.Substring(PREFIX.Length)] = element.Value; + } + } + + return result; + } + + private void ProcessCollectionReturn(NativeSQLQueryCollectionReturn collectionReturn) + { + // we are initializing an owned collection + string role = collectionReturn.OwnerEntityName + '.' + collectionReturn.OwnerProperty; + AddCollection(role, collectionReturn.Alias, collectionReturn.PropertyResultsMap); + } + + private void ProcessJoinReturn(NativeSQLQueryJoinReturn fetchReturn) + { + string alias = fetchReturn.Alias; + if (alias2EntityPersister.ContainsKey(alias) || alias2CollectionPersister.ContainsKey(alias)) + { + // already been processed... + return; + } + + string ownerAlias = fetchReturn.OwnerAlias; + + // Make sure the owner alias is known... + INativeSQLQueryReturn ownerReturn; + if (!alias2Return.TryGetValue(ownerAlias, out ownerReturn)) + { + throw new HibernateException(string.Format("Owner alias [{0}] is unknown for alias [{1}]", ownerAlias, alias)); + } + + // If this return's alias has not been processed yet, do so b4 further processing of this return + if (!alias2EntityPersister.ContainsKey(ownerAlias)) + { + ProcessReturn(ownerReturn); + } + + var ownerPersister = alias2EntityPersister[ownerAlias]; + var returnType = ownerPersister.GetPropertyType(fetchReturn.OwnerProperty); + + if (returnType.IsCollectionType) + { + string role = ownerPersister.EntityName + '.' + fetchReturn.OwnerProperty; + AddCollection(role, alias, fetchReturn.PropertyResultsMap); + } + else if (returnType.IsEntityType) + { + EntityType eType = (EntityType) returnType; + string returnEntityName = eType.GetAssociatedEntityName(); + ISqlLoadable persister = GetSQLLoadable(returnEntityName); + AddPersister(alias, fetchReturn.PropertyResultsMap, persister); + } + } + + #endregion + } +} diff --git a/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs b/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs index 95943273a32..ab43b18b2a4 100644 --- a/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs +++ b/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -12,27 +11,16 @@ namespace NHibernate.Loader.Custom.Sql { - public class SQLQueryParser + internal class SQLQueryParser { - public interface IParserContext - { - bool IsEntityAlias(string aliasName); - ISqlLoadable GetEntityPersisterByAlias(string alias); - string GetEntitySuffixByAlias(string alias); - bool IsCollectionAlias(string aliasName); - ISqlLoadableCollection GetCollectionPersisterByAlias(string alias); - string GetCollectionSuffixByAlias(string alias); - IDictionary GetPropertyResultsMapByAlias(string alias); - } - private readonly ISessionFactoryImplementor factory; private readonly string originalQueryString; - private readonly IParserContext context; + private readonly SQLQueryContext context; private long aliasesFound; private IEnumerable parametersSpecifications; - public SQLQueryParser(ISessionFactoryImplementor factory, string sqlQuery, IParserContext context) + public SQLQueryParser(ISessionFactoryImplementor factory, string sqlQuery, SQLQueryContext context) { this.factory = factory; originalQueryString = sqlQuery; @@ -54,7 +42,7 @@ public IEnumerable CollectedParametersSpecifications get { return parametersSpecifications; } } - // TODO: should "record" how many properties we have reffered to - and if we + // TODO: should "record" how many properties we have refered to - and if we // don't get'em'all we throw an exception! Way better than trial and error ;) private string SubstituteBrackets() { @@ -132,16 +120,16 @@ private string SubstituteBrackets() private string ResolveCollectionProperties(string aliasName, string propertyName) { - IDictionary fieldResults = context.GetPropertyResultsMapByAlias(aliasName); - ISqlLoadableCollection collectionPersister = context.GetCollectionPersisterByAlias(aliasName); - string collectionSuffix = context.GetCollectionSuffixByAlias(aliasName); + IDictionary fieldResults = context.GetCollectionPropertyResultsMap(aliasName); + ISqlLoadableCollection collectionPersister = context.GetCollectionPersister(aliasName); + string collectionSuffix = context.GetCollectionSuffix(aliasName); // NH Different behavior for NH-1612 if ("*".Equals(propertyName)) { - if (fieldResults.Count != 0) + if (fieldResults != null) { - throw new QueryException("Using return-propertys together with * syntax is not supported."); + throw new QueryException("Using return-property elements together with * syntax is not supported."); } string selectFragment = collectionPersister.SelectFragment(aliasName, collectionSuffix); @@ -162,7 +150,7 @@ private string ResolveCollectionProperties(string aliasName, string propertyName { return ResolveProperties(aliasName, elementPropertyName); } - else if (elementPropertyName == "*") + if (elementPropertyName == "*") { throw new QueryException("Using element.* syntax is only supported for entity elements."); } @@ -171,7 +159,7 @@ private string ResolveCollectionProperties(string aliasName, string propertyName string[] columnAliases; // Let return-propertys override whatever the persister has for aliases. - if (!fieldResults.TryGetValue(propertyName, out columnAliases)) + if (fieldResults == null || !fieldResults.TryGetValue(propertyName, out columnAliases)) { columnAliases = collectionPersister.GetCollectionPropertyColumnAliases(propertyName, collectionSuffix); } @@ -194,44 +182,43 @@ private string ResolveCollectionProperties(string aliasName, string propertyName private string ResolveProperties(string aliasName, string propertyName) { - IDictionary fieldResults = context.GetPropertyResultsMapByAlias(aliasName); - ISqlLoadable persister = context.GetEntityPersisterByAlias(aliasName); - string suffix = context.GetEntitySuffixByAlias(aliasName); + IDictionary fieldResults = context.GetEntityPropertyResultsMap(aliasName); + ISqlLoadable persister = context.GetEntityPersister(aliasName); + string suffix = context.GetEntitySuffix(aliasName); if ("*".Equals(propertyName)) { - if (fieldResults.Count != 0) + if (fieldResults != null) { - throw new QueryException("Using return-propertys together with * syntax is not supported."); + throw new QueryException("Using return-property elements together with * syntax is not supported."); } aliasesFound++; return persister.SelectFragment(aliasName, suffix); } - else - { - string[] columnAliases; - // Let return-propertys override whatever the persister has for aliases. - if (!fieldResults.TryGetValue(propertyName, out columnAliases)) - { - columnAliases = persister.GetSubclassPropertyColumnAliases(propertyName, suffix); - } + string[] columnAliases; - if (columnAliases == null || columnAliases.Length == 0) - { - throw new QueryException("No column name found for property [" + propertyName + "] for alias [" + aliasName + "]", - originalQueryString); - } - if (columnAliases.Length != 1) - { - // TODO: better error message since we actually support composites if names are explicitly listed. - throw new QueryException( - "SQL queries only support properties mapped to a single column - property [" + propertyName + "] is mapped to " - + columnAliases.Length + " columns.", originalQueryString); - } - aliasesFound++; - return columnAliases[0]; + // Let return-property elements override whatever the persister has for aliases. + if (fieldResults == null || !fieldResults.TryGetValue(propertyName, out columnAliases)) + { + columnAliases = persister.GetSubclassPropertyColumnAliases(propertyName, suffix); + } + + if (columnAliases == null || columnAliases.Length == 0) + { + throw new QueryException("No column name found for property [" + propertyName + "] for alias [" + aliasName + "]", + originalQueryString); + } + if (columnAliases.Length != 1) + { + // TODO: better error message since we actually support composites if names are explicitly listed. + throw new QueryException( + "SQL queries only support properties mapped to a single column - property [" + propertyName + "] is mapped to " + + columnAliases.Length + " columns.", originalQueryString); } + aliasesFound++; + + return columnAliases[0]; } /// diff --git a/src/NHibernate/Loader/Custom/Sql/SQLQueryReturnProcessor.cs b/src/NHibernate/Loader/Custom/Sql/SQLQueryReturnProcessor.cs deleted file mode 100644 index 94ce34aa2d9..00000000000 --- a/src/NHibernate/Loader/Custom/Sql/SQLQueryReturnProcessor.cs +++ /dev/null @@ -1,410 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; - -using NHibernate.Engine; -using NHibernate.Engine.Query.Sql; -using NHibernate.Persister.Collection; -using NHibernate.Persister.Entity; -using NHibernate.Type; - -namespace NHibernate.Loader.Custom.Sql -{ - public class SQLQueryReturnProcessor - { - private static readonly INHibernateLogger log = NHibernateLogger.For(typeof (SQLQueryReturnProcessor)); - - private readonly INativeSQLQueryReturn[] queryReturns; - - private readonly Dictionary alias2Return = - new Dictionary(); - - private readonly Dictionary alias2OwnerAlias = new Dictionary(); - - private readonly Dictionary alias2Persister = new Dictionary(); - private readonly Dictionary alias2Suffix = new Dictionary(); - - private readonly Dictionary alias2CollectionPersister = - new Dictionary(); - - private readonly Dictionary alias2CollectionSuffix = new Dictionary(); - - private readonly Dictionary> entityPropertyResultMaps = - new Dictionary>(); - - private readonly Dictionary> collectionPropertyResultMaps = - new Dictionary>(); - - private readonly ISessionFactoryImplementor factory; - - private int entitySuffixSeed = 0; - private int collectionSuffixSeed = 0; - - private ISessionFactoryImplementor Factory - { - get { return factory; } - } - - public SQLQueryReturnProcessor(INativeSQLQueryReturn[] queryReturns, ISessionFactoryImplementor factory) - { - this.queryReturns = queryReturns; - this.factory = factory; - } - - public class ResultAliasContext - { - private readonly SQLQueryReturnProcessor parent; - - public ResultAliasContext(SQLQueryReturnProcessor parent) - { - this.parent = parent; - } - - public ISqlLoadable GetEntityPersister(string alias) - { - ISqlLoadable result; - parent.alias2Persister.TryGetValue(alias, out result); - return result; - } - - public ISqlLoadableCollection GetCollectionPersister(string alias) - { - ISqlLoadableCollection result; - parent.alias2CollectionPersister.TryGetValue(alias, out result); - return result; - } - - public string GetEntitySuffix(string alias) - { - string result; - parent.alias2Suffix.TryGetValue(alias, out result); - return result; - } - - public string GetCollectionSuffix(string alias) - { - string result; - parent.alias2CollectionSuffix.TryGetValue(alias, out result); - return result; - } - - public string GetOwnerAlias(string alias) - { - string result; - parent.alias2OwnerAlias.TryGetValue(alias, out result); - return result; - } - - public IDictionary GetPropertyResultsMap(string alias) - { - return parent.InternalGetPropertyResultsMap(alias); - } - } - - private IDictionary InternalGetPropertyResultsMap(string alias) - { - NativeSQLQueryNonScalarReturn rtn = alias2Return[alias] as NativeSQLQueryNonScalarReturn; - return rtn != null ? rtn.PropertyResultsMap : null; - } - - private bool HasPropertyResultMap(string alias) - { - IDictionary propertyMaps = InternalGetPropertyResultsMap(alias); - return propertyMaps != null && propertyMaps.Count != 0; - } - - public ResultAliasContext Process() - { - // first, break down the returns into maps keyed by alias - // so that role returns can be more easily resolved to their owners - for (int i = 0; i < queryReturns.Length; i++) - { - var rtn = queryReturns[i] as NativeSQLQueryNonScalarReturn; - if (rtn != null) - { - alias2Return[rtn.Alias] = rtn; - var roleReturn = queryReturns[i] as NativeSQLQueryJoinReturn; - if (roleReturn != null) - { - alias2OwnerAlias[roleReturn.Alias] = roleReturn.OwnerAlias; - } - } - } - - // Now, process the returns - for (int i = 0; i < queryReturns.Length; i++) - { - ProcessReturn(queryReturns[i]); - } - - return new ResultAliasContext(this); - } - - private ISqlLoadable GetSQLLoadable(string entityName) - { - IEntityPersister persister = factory.GetEntityPersister(entityName); - var persisterAsSqlLoadable = persister as ISqlLoadable; - if (persisterAsSqlLoadable == null) - { - throw new MappingException("class persister is not ISqlLoadable: " + entityName); - } - return persisterAsSqlLoadable; - } - - private string GenerateEntitySuffix() - { - return BasicLoader.GenerateSuffixes(entitySuffixSeed++, 1)[0]; - } - - private string GenerateCollectionSuffix() - { - return collectionSuffixSeed++ + "__"; - } - - private void ProcessReturn(INativeSQLQueryReturn rtn) - { - if (rtn is NativeSQLQueryScalarReturn) - { - ProcessScalarReturn((NativeSQLQueryScalarReturn) rtn); - } - else if (rtn is NativeSQLQueryRootReturn) - { - ProcessRootReturn((NativeSQLQueryRootReturn) rtn); - } - else if (rtn is NativeSQLQueryCollectionReturn) - { - ProcessCollectionReturn((NativeSQLQueryCollectionReturn) rtn); - } - else - { - ProcessJoinReturn((NativeSQLQueryJoinReturn) rtn); - } - } - - private void ProcessScalarReturn(NativeSQLQueryScalarReturn typeReturn) {} - - private void ProcessRootReturn(NativeSQLQueryRootReturn rootReturn) - { - if (alias2Persister.ContainsKey(rootReturn.Alias)) - { - // already been processed... - return; - } - - ISqlLoadable persister = GetSQLLoadable(rootReturn.ReturnEntityName); - AddPersister(rootReturn.Alias, rootReturn.PropertyResultsMap, persister); - } - - private void AddPersister(string alias, IDictionary propertyResult, ISqlLoadable persister) - { - alias2Persister[alias] = persister; - string suffix = GenerateEntitySuffix(); - log.Debug("mapping alias [{0}] to entity-suffix [{1}]", alias, suffix); - alias2Suffix[alias] = suffix; - entityPropertyResultMaps[alias] = propertyResult; - } - - private void AddCollection(string role, string alias, IDictionary propertyResults) - { - ISqlLoadableCollection collectionPersister = (ISqlLoadableCollection) Factory.GetCollectionPersister(role); - alias2CollectionPersister[alias] = collectionPersister; - string suffix = GenerateCollectionSuffix(); - log.Debug("mapping alias [{0}] to collection-suffix [{1}]", alias, suffix); - alias2CollectionSuffix[alias] = suffix; - collectionPropertyResultMaps[alias] = propertyResults; - - if (collectionPersister.IsOneToMany) - { - ISqlLoadable persister = (ISqlLoadable) collectionPersister.ElementPersister; - AddPersister(alias, Filter(propertyResults), persister); - } - } - - private IDictionary Filter(IDictionary propertyResults) - { - Dictionary result = new Dictionary(propertyResults.Count); - - string keyPrefix = "element."; - - foreach (KeyValuePair element in propertyResults) - { - string path = element.Key; - if (path.StartsWith(keyPrefix, StringComparison.Ordinal)) - { - result[path.Substring(keyPrefix.Length)] = element.Value; - } - } - - return result; - } - - private void ProcessCollectionReturn(NativeSQLQueryCollectionReturn collectionReturn) - { - // we are initializing an owned collection - string role = collectionReturn.OwnerEntityName + '.' + collectionReturn.OwnerProperty; - AddCollection(role, collectionReturn.Alias, collectionReturn.PropertyResultsMap); - } - - private void ProcessJoinReturn(NativeSQLQueryJoinReturn fetchReturn) - { - string alias = fetchReturn.Alias; - if (alias2Persister.ContainsKey(alias) || alias2CollectionPersister.ContainsKey(alias)) - { - // already been processed... - return; - } - - string ownerAlias = fetchReturn.OwnerAlias; - - // Make sure the owner alias is known... - if (!alias2Return.ContainsKey(ownerAlias)) - { - throw new HibernateException(string.Format("Owner alias [{0}] is unknown for alias [{1}]", ownerAlias, alias)); - } - - // If this return's alias has not been processed yet, do so b4 further processing of this return - if (!alias2Persister.ContainsKey(ownerAlias)) - { - ProcessReturn(alias2Return[ownerAlias]); - } - - ISqlLoadable ownerPersister = alias2Persister[ownerAlias]; - IType returnType = ownerPersister.GetPropertyType(fetchReturn.OwnerProperty); - - if (returnType.IsCollectionType) - { - string role = ownerPersister.EntityName + '.' + fetchReturn.OwnerProperty; - AddCollection(role, alias, fetchReturn.PropertyResultsMap); - } - else if (returnType.IsEntityType) - { - EntityType eType = (EntityType) returnType; - string returnEntityName = eType.GetAssociatedEntityName(); - ISqlLoadable persister = GetSQLLoadable(returnEntityName); - AddPersister(alias, fetchReturn.PropertyResultsMap, persister); - } - } - - public IList GenerateCustomReturns(bool queryHadAliases) - { - IList customReturns = new List(); - IDictionary customReturnsByAlias = new Dictionary(); - for (int i = 0; i < queryReturns.Length; i++) - { - if (queryReturns[i] is NativeSQLQueryScalarReturn) - { - NativeSQLQueryScalarReturn rtn = (NativeSQLQueryScalarReturn) queryReturns[i]; - customReturns.Add(new ScalarReturn(rtn.Type, rtn.ColumnAlias)); - } - else if (queryReturns[i] is NativeSQLQueryRootReturn) - { - NativeSQLQueryRootReturn rtn = (NativeSQLQueryRootReturn) queryReturns[i]; - string alias = rtn.Alias; - IEntityAliases entityAliases; - if (queryHadAliases || HasPropertyResultMap(alias)) - { - entityAliases = - new DefaultEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]); - } - else - { - entityAliases = - new ColumnEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]); - } - RootReturn customReturn = new RootReturn(alias, rtn.ReturnEntityName, entityAliases, rtn.LockMode); - customReturns.Add(customReturn); - customReturnsByAlias[rtn.Alias] = customReturn; - } - else if (queryReturns[i] is NativeSQLQueryCollectionReturn) - { - NativeSQLQueryCollectionReturn rtn = (NativeSQLQueryCollectionReturn) queryReturns[i]; - string alias = rtn.Alias; - ISqlLoadableCollection persister = alias2CollectionPersister[alias]; - bool isEntityElements = persister.ElementType.IsEntityType; - ICollectionAliases collectionAliases; - IEntityAliases elementEntityAliases = null; - if (queryHadAliases || HasPropertyResultMap(alias)) - { - collectionAliases = - new GeneratedCollectionAliases(collectionPropertyResultMaps[alias], alias2CollectionPersister[alias], - alias2CollectionSuffix[alias]); - if (isEntityElements) - { - elementEntityAliases = - new DefaultEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]); - } - } - else - { - collectionAliases = - new ColumnCollectionAliases(collectionPropertyResultMaps[alias], alias2CollectionPersister[alias]); - if (isEntityElements) - { - elementEntityAliases = - new ColumnEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]); - } - } - CollectionReturn customReturn = - new CollectionReturn(alias, rtn.OwnerEntityName, rtn.OwnerProperty, collectionAliases, elementEntityAliases, - rtn.LockMode); - customReturns.Add(customReturn); - customReturnsByAlias[rtn.Alias] = customReturn; - } - else if (queryReturns[i] is NativeSQLQueryJoinReturn) - { - NativeSQLQueryJoinReturn rtn = (NativeSQLQueryJoinReturn) queryReturns[i]; - string alias = rtn.Alias; - FetchReturn customReturn; - NonScalarReturn ownerCustomReturn = (NonScalarReturn) customReturnsByAlias[rtn.OwnerAlias]; - ISqlLoadableCollection persister; - if (alias2CollectionPersister.TryGetValue(alias, out persister)) - { - bool isEntityElements = persister.ElementType.IsEntityType; - ICollectionAliases collectionAliases; - IEntityAliases elementEntityAliases = null; - if (queryHadAliases || HasPropertyResultMap(alias)) - { - collectionAliases = - new GeneratedCollectionAliases(collectionPropertyResultMaps[alias], persister, alias2CollectionSuffix[alias]); - if (isEntityElements) - { - elementEntityAliases = - new DefaultEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]); - } - } - else - { - collectionAliases = new ColumnCollectionAliases(collectionPropertyResultMaps[alias], persister); - if (isEntityElements) - { - elementEntityAliases = - new ColumnEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]); - } - } - customReturn = - new CollectionFetchReturn(alias, ownerCustomReturn, rtn.OwnerProperty, collectionAliases, elementEntityAliases, - rtn.LockMode); - } - else - { - IEntityAliases entityAliases; - if (queryHadAliases || HasPropertyResultMap(alias)) - { - entityAliases = - new DefaultEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]); - } - else - { - entityAliases = - new ColumnEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]); - } - customReturn = new EntityFetchReturn(alias, entityAliases, ownerCustomReturn, rtn.OwnerProperty, rtn.LockMode); - } - customReturns.Add(customReturn); - customReturnsByAlias[alias] = customReturn; - } - } - return customReturns; - } - } -} From c30383f350c5dc7344068ff37d5ad1ef32f7acc5 Mon Sep 17 00:00:00 2001 From: Gerke Geurts Date: Wed, 24 Oct 2012 13:59:45 +0200 Subject: [PATCH 2/3] Fix custom component property mapping issue --- .../SqlTest/Query/NativeSQLQueriesFixture.cs | 175 +++++++++++------- .../SqlTest/Query/NativeSQLQueries.hbm.xml | 103 ++++++----- .../SqlTest/Query/NativeSQLQueriesFixture.cs | 167 ++++++++++------- .../Loader/Custom/ColumnCollectionAliases.cs | 1 - .../Loader/Custom/Sql/SQLQueryContext.cs | 80 +++++++- 5 files changed, 340 insertions(+), 186 deletions(-) diff --git a/src/NHibernate.Test/Async/SqlTest/Query/NativeSQLQueriesFixture.cs b/src/NHibernate.Test/Async/SqlTest/Query/NativeSQLQueriesFixture.cs index 33f0f9b318d..4dcafbfea68 100644 --- a/src/NHibernate.Test/Async/SqlTest/Query/NativeSQLQueriesFixture.cs +++ b/src/NHibernate.Test/Async/SqlTest/Query/NativeSQLQueriesFixture.cs @@ -8,34 +8,20 @@ //------------------------------------------------------------------------------ +using System; using System.Collections; using System.Linq; +using NHibernate.Criterion; using NHibernate.Transform; using NUnit.Framework; -using NHibernate.Criterion; namespace NHibernate.Test.SqlTest.Query { using System.Threading.Tasks; + using System.Threading; [TestFixture] public class GeneralTestAsync : TestCase { - protected const string OrganizationFetchJoinEmploymentSQL = - "SELECT org.ORGID as {org.id}, " + - " org.NAME as {org.name}, " + - " emp.EMPLOYER as {emp.key}, " + - " emp.EMPID as {emp.element}, " + - " {emp.element.*} " + - "FROM ORGANIZATION org " + - " LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER"; - - protected const string OrganizationJoinEmploymentSQL = - "SELECT org.ORGID as {org.id}, " + - " org.NAME as {org.name}, " + - " {emp.*} " + - "FROM ORGANIZATION org " + - " LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER"; - protected const string EmploymentSQL = "SELECT * FROM EMPLOYMENT"; protected string EmploymentSQLMixedScalarEntity = @@ -306,24 +292,6 @@ public async Task MappedAliasStrategyAsync() await (t.CommitAsync()); s.Close(); - if (TestDialect.SupportsDuplicatedColumnAliases) - { - s = OpenSession(); - t = s.BeginTransaction(); - sqlQuery = s.GetNamedQuery("organizationreturnproperty"); - sqlQuery.SetResultTransformer(CriteriaSpecification.AliasToEntityMap); - list = await (sqlQuery.ListAsync()); - Assert.AreEqual(2, list.Count); - m = (IDictionary) list[0]; - Assert.IsTrue(m.Contains("org")); - AssertClassAssignability(m["org"].GetType(), typeof(Organization)); - Assert.IsTrue(m.Contains("emp")); - AssertClassAssignability(m["emp"].GetType(), typeof(Employment)); - Assert.AreEqual(2, m.Count); - await (t.CommitAsync()); - s.Close(); - } - s = OpenSession(); t = s.BeginTransaction(); namedQuery = s.GetNamedQuery("EmploymentAndPerson"); @@ -469,9 +437,9 @@ public async Task AutoDetectAliasingAsync() // TODO H3: H3.2 can guess the return column type so they can use just addScalar("employerid"), // but NHibernate currently can't do it. - list = - await (s.CreateSQLQuery(EmploymentSQLMixedScalarEntity).AddScalar("employerid", NHibernateUtil.Int64).AddEntity( - typeof(Employment)).ListAsync()); + list = await (s.CreateSQLQuery(EmploymentSQLMixedScalarEntity) + .AddScalar("employerid", NHibernateUtil.Int64) + .AddEntity(typeof(Employment)).ListAsync()); Assert.AreEqual(1, list.Count); o = (object[]) list[0]; Assert.AreEqual(2, o.Length); @@ -484,37 +452,6 @@ public async Task AutoDetectAliasingAsync() list = await (queryWithCollection.ListAsync()); Assert.AreEqual(list.Count, 1); - s.Clear(); - - list = await (s.CreateSQLQuery(OrganizationJoinEmploymentSQL) - .AddEntity("org", typeof(Organization)) - .AddJoin("emp", "org.employments") - .ListAsync()); - Assert.AreEqual(2, list.Count); - - s.Clear(); - - list = await (s.CreateSQLQuery(OrganizationFetchJoinEmploymentSQL) - .AddEntity("org", typeof(Organization)) - .AddJoin("emp", "org.employments") - .ListAsync()); - Assert.AreEqual(2, list.Count); - - s.Clear(); - - if (TestDialect.SupportsDuplicatedColumnAliases) - { - // TODO : why twice? - await (s.GetNamedQuery("organizationreturnproperty").ListAsync()); - list = await (s.GetNamedQuery("organizationreturnproperty").ListAsync()); - Assert.AreEqual(2, list.Count); - - s.Clear(); - - list = await (s.GetNamedQuery("organizationautodetect").ListAsync()); - Assert.AreEqual(2, list.Count); - } - await (t.CommitAsync()); s.Close(); @@ -558,6 +495,106 @@ public async Task AutoDetectAliasingAsync() s.Close(); } + public Task CanQueryWithGeneratedAliasesOnly_UsingWildcardAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + const string SQL = + "SELECT org.ORGID as {org.id}, " + + " org.NAME as {org.name}, " + + " {emp.*} " + + "FROM ORGANIZATION org " + + " LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER"; + + return VerifyOrganisationQueryAsync(session => session.CreateSQLQuery(SQL) + .AddEntity("org", typeof(Organization)) + .AddJoin("emp", "org.employments"), cancellationToken); + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + [Test] + public async Task CanQueryWithGeneratedAliasesOnly_UsingCollectionElementWildcardAsync() + { + const string SQL = + "SELECT org.ORGID as {org.id}, " + + " org.NAME as {org.name}, " + + " emp.EMPLOYER as {emp.key}, " + + " emp.EMPID as {emp.element}, " + + " {emp.element.*} " + + "FROM ORGANIZATION org " + + " LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER"; + + await (VerifyOrganisationQueryAsync(session => session.CreateSQLQuery(SQL) + .AddEntity("org", typeof(Organization)) + .AddJoin("emp", "org.employments"))); + } + + [Test] + public async Task CanQueryWithColumnNamesOnlyAsync() + { + await (VerifyOrganisationQueryAsync(session => session.GetNamedQuery("organization-using-manual-aliases"))); + } + + [Test] + public async Task CanQueryWithManualAliasesOnlyAsync() + { + await (VerifyOrganisationQueryAsync(session => session.GetNamedQuery("organization-using-column-names"))); + } + + [Test] + public async Task CanQueryWithMixOfColumnNamesAndManualAliasesAsync() + { + await (VerifyOrganisationQueryAsync(session => session.GetNamedQuery("organization-using-column-names-and-manual-aliases"))); + } + + private async Task VerifyOrganisationQueryAsync(Func queryFactory, CancellationToken cancellationToken = default(CancellationToken)) + { + using (ISession s = OpenSession()) + using (ITransaction t = s.BeginTransaction()) + { + var ifa = new Organization("IFA"); + var jboss = new Organization("JBoss"); + var gavin = new Person("Gavin"); + var emp = new Employment(gavin, jboss, "AU"); + await (s.SaveAsync(jboss, cancellationToken)); + await (s.SaveAsync(ifa, cancellationToken)); + await (s.SaveAsync(gavin, cancellationToken)); + await (s.SaveAsync(emp, cancellationToken)); + + var list = await (queryFactory(s).ListAsync(cancellationToken)); + Assert.AreEqual(2, list.Count); + } + } + + [Test] + public async Task CanQueryWithManualComponentPropertyAliasesAsync() + { + using (ISession s = OpenSession()) + using (ITransaction t = s.BeginTransaction()) + { + SpaceShip enterprise = new SpaceShip(); + enterprise.Model = "USS"; + enterprise.Name = "Entreprise"; + enterprise.Speed = 50d; + Dimension d = new Dimension(45, 10); + enterprise.Dimensions = d; + await (s.SaveAsync(enterprise)); + + await (s.FlushAsync()); + s.Clear(); + + object[] result = (object[])await (s.GetNamedQuery("spaceship").UniqueResultAsync()); + enterprise = (SpaceShip)result[0]; + Assert.AreEqual(50d, enterprise.Speed, "Speed"); + Assert.AreEqual(45, enterprise.Dimensions.Length, "Dimensions.Length"); + Assert.AreEqual(10, enterprise.Dimensions.Width, "Dimensions.Width"); + } + } + [Test] public async Task MixAndMatchEntityScalarAsync() { diff --git a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueries.hbm.xml b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueries.hbm.xml index 08590d63b2a..929bd86fa7d 100644 --- a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueries.hbm.xml +++ b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueries.hbm.xml @@ -1,13 +1,13 @@  + namespace="NHibernate.Test.SqlTest" + assembly="NHibernate.Test"> @@ -27,7 +27,6 @@ - @@ -116,7 +115,6 @@ - @@ -149,14 +147,14 @@ fld_name as name, fld_model as model, fld_speed as speed, - fld_length as flength, + fld_length as length, fld_width as width, fld_length * fld_width as surface, fld_length * fld_width *10 as volume from SpaceShip - + SELECT org.NAME FROM ORGANIZATION org @@ -204,56 +202,75 @@ ORDER BY STARTDATE ASC, EMPLOYEE ASC - + - - + + - - - - - - - - + + + + + + + + - - + + - SELECT org.ORGID as orgid, - org.NAME as name, - emp.EMPLOYER as employer, - emp.EMPID as empid, - emp.EMPLOYEE as employee, - emp.EMPLOYER as employer, - emp.STARTDATE as xstartDate, - emp.ENDDATE as endDate, - emp.REGIONCODE as regionCode, - emp.AVALUE as AVALUE, - emp.CURRENCY as CURRENCY + SELECT org.ORGID as org_id, + org.NAME as org_name, + emp.EMPLOYER as emp_employer, + emp.EMPID as emp_id, + emp.EMPLOYEE as emp_employee, + emp.STARTDATE as emp_startDate, + emp.ENDDATE as emp_endDate, + emp.REGIONCODE as emp_regionCode, + emp.AVALUE as emp_avalue, + emp.CURRENCY as emp_currency FROM ORGANIZATION org LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER - - + - SELECT org.ORGID as orgid, - org.NAME as name, - emp.EMPLOYER as employer, - emp.EMPID as empid, - emp.EMPLOYEE as employee, - emp.EMPLOYER as employer, - emp.STARTDATE as startDate, - emp.ENDDATE as endDate, - emp.REGIONCODE as regionCode, + SELECT org.ORGID as ORGID, + org.NAME as NAME, + emp.EMPLOYER as EMPLOYER, + emp.EMPID as EMPID, + emp.EMPLOYEE as EMPLOYEE, + emp.STARTDATE as STARTDATE, + emp.ENDDATE as ENDDATE, + emp.REGIONCODE as REGIONCODE, emp.AVALUE as AVALUE, emp.CURRENCY as CURRENCY FROM ORGANIZATION org LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER + + + + + + + + + + SELECT org.ORGID as ORGANISATION_ID, + org.NAME as NAME, + emp.EMPID as EMPLOYMENT_ID, + emp.EMPLOYEE as EMPLOYEE, + emp.EMPLOYER as EMPLOYER, + emp.STARTDATE as STARTDATE, + emp.ENDDATE as ENDDATE, + emp.REGIONCODE as REGIONCODE, + emp.AVALUE as AVALUE, + emp.CURRENCY as CURRENCY + FROM ORGANIZATION org + LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER + diff --git a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs index e89bd35775f..d59cf3ebfa2 100644 --- a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs +++ b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs @@ -1,30 +1,15 @@ +using System; using System.Collections; using System.Linq; +using NHibernate.Criterion; using NHibernate.Transform; using NUnit.Framework; -using NHibernate.Criterion; namespace NHibernate.Test.SqlTest.Query { [TestFixture] public class GeneralTest : TestCase { - protected const string OrganizationFetchJoinEmploymentSQL = - "SELECT org.ORGID as {org.id}, " + - " org.NAME as {org.name}, " + - " emp.EMPLOYER as {emp.key}, " + - " emp.EMPID as {emp.element}, " + - " {emp.element.*} " + - "FROM ORGANIZATION org " + - " LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER"; - - protected const string OrganizationJoinEmploymentSQL = - "SELECT org.ORGID as {org.id}, " + - " org.NAME as {org.name}, " + - " {emp.*} " + - "FROM ORGANIZATION org " + - " LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER"; - protected const string EmploymentSQL = "SELECT * FROM EMPLOYMENT"; protected string EmploymentSQLMixedScalarEntity = @@ -295,24 +280,6 @@ public void MappedAliasStrategy() t.Commit(); s.Close(); - if (TestDialect.SupportsDuplicatedColumnAliases) - { - s = OpenSession(); - t = s.BeginTransaction(); - sqlQuery = s.GetNamedQuery("organizationreturnproperty"); - sqlQuery.SetResultTransformer(CriteriaSpecification.AliasToEntityMap); - list = sqlQuery.List(); - Assert.AreEqual(2, list.Count); - m = (IDictionary) list[0]; - Assert.IsTrue(m.Contains("org")); - AssertClassAssignability(m["org"].GetType(), typeof(Organization)); - Assert.IsTrue(m.Contains("emp")); - AssertClassAssignability(m["emp"].GetType(), typeof(Employment)); - Assert.AreEqual(2, m.Count); - t.Commit(); - s.Close(); - } - s = OpenSession(); t = s.BeginTransaction(); namedQuery = s.GetNamedQuery("EmploymentAndPerson"); @@ -458,9 +425,9 @@ public void AutoDetectAliasing() // TODO H3: H3.2 can guess the return column type so they can use just addScalar("employerid"), // but NHibernate currently can't do it. - list = - s.CreateSQLQuery(EmploymentSQLMixedScalarEntity).AddScalar("employerid", NHibernateUtil.Int64).AddEntity( - typeof(Employment)).List(); + list = s.CreateSQLQuery(EmploymentSQLMixedScalarEntity) + .AddScalar("employerid", NHibernateUtil.Int64) + .AddEntity(typeof(Employment)).List(); Assert.AreEqual(1, list.Count); o = (object[]) list[0]; Assert.AreEqual(2, o.Length); @@ -473,37 +440,6 @@ public void AutoDetectAliasing() list = queryWithCollection.List(); Assert.AreEqual(list.Count, 1); - s.Clear(); - - list = s.CreateSQLQuery(OrganizationJoinEmploymentSQL) - .AddEntity("org", typeof(Organization)) - .AddJoin("emp", "org.employments") - .List(); - Assert.AreEqual(2, list.Count); - - s.Clear(); - - list = s.CreateSQLQuery(OrganizationFetchJoinEmploymentSQL) - .AddEntity("org", typeof(Organization)) - .AddJoin("emp", "org.employments") - .List(); - Assert.AreEqual(2, list.Count); - - s.Clear(); - - if (TestDialect.SupportsDuplicatedColumnAliases) - { - // TODO : why twice? - s.GetNamedQuery("organizationreturnproperty").List(); - list = s.GetNamedQuery("organizationreturnproperty").List(); - Assert.AreEqual(2, list.Count); - - s.Clear(); - - list = s.GetNamedQuery("organizationautodetect").List(); - Assert.AreEqual(2, list.Count); - } - t.Commit(); s.Close(); @@ -547,6 +483,99 @@ public void AutoDetectAliasing() s.Close(); } + public void CanQueryWithGeneratedAliasesOnly_UsingWildcard() + { + const string SQL = + "SELECT org.ORGID as {org.id}, " + + " org.NAME as {org.name}, " + + " {emp.*} " + + "FROM ORGANIZATION org " + + " LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER"; + + VerifyOrganisationQuery(session => session.CreateSQLQuery(SQL) + .AddEntity("org", typeof(Organization)) + .AddJoin("emp", "org.employments")); + } + + [Test] + public void CanQueryWithGeneratedAliasesOnly_UsingCollectionElementWildcard() + { + const string SQL = + "SELECT org.ORGID as {org.id}, " + + " org.NAME as {org.name}, " + + " emp.EMPLOYER as {emp.key}, " + + " emp.EMPID as {emp.element}, " + + " {emp.element.*} " + + "FROM ORGANIZATION org " + + " LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER"; + + VerifyOrganisationQuery(session => session.CreateSQLQuery(SQL) + .AddEntity("org", typeof(Organization)) + .AddJoin("emp", "org.employments")); + } + + [Test] + public void CanQueryWithColumnNamesOnly() + { + VerifyOrganisationQuery(session => session.GetNamedQuery("organization-using-manual-aliases")); + } + + [Test] + public void CanQueryWithManualAliasesOnly() + { + VerifyOrganisationQuery(session => session.GetNamedQuery("organization-using-column-names")); + } + + [Test] + public void CanQueryWithMixOfColumnNamesAndManualAliases() + { + VerifyOrganisationQuery(session => session.GetNamedQuery("organization-using-column-names-and-manual-aliases")); + } + + private void VerifyOrganisationQuery(Func queryFactory) + { + using (ISession s = OpenSession()) + using (ITransaction t = s.BeginTransaction()) + { + var ifa = new Organization("IFA"); + var jboss = new Organization("JBoss"); + var gavin = new Person("Gavin"); + var emp = new Employment(gavin, jboss, "AU"); + s.Save(jboss); + s.Save(ifa); + s.Save(gavin); + s.Save(emp); + + var list = queryFactory(s).List(); + Assert.AreEqual(2, list.Count); + } + } + + [Test] + public void CanQueryWithManualComponentPropertyAliases() + { + using (ISession s = OpenSession()) + using (ITransaction t = s.BeginTransaction()) + { + SpaceShip enterprise = new SpaceShip(); + enterprise.Model = "USS"; + enterprise.Name = "Entreprise"; + enterprise.Speed = 50d; + Dimension d = new Dimension(45, 10); + enterprise.Dimensions = d; + s.Save(enterprise); + + s.Flush(); + s.Clear(); + + object[] result = (object[])s.GetNamedQuery("spaceship").UniqueResult(); + enterprise = (SpaceShip)result[0]; + Assert.AreEqual(50d, enterprise.Speed, "Speed"); + Assert.AreEqual(45, enterprise.Dimensions.Length, "Dimensions.Length"); + Assert.AreEqual(10, enterprise.Dimensions.Width, "Dimensions.Width"); + } + } + [Test] public void MixAndMatchEntityScalar() { diff --git a/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs b/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs index 111a91f3835..b0cd2486556 100644 --- a/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs +++ b/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs @@ -23,7 +23,6 @@ public ColumnCollectionAliases(IDictionary userProvidedAliases this.keyAliases = GetUserProvidedAliases("key", persister.KeyColumnNames); this.indexAliases = GetUserProvidedAliases("index", persister.IndexColumnNames); - this.elementAliases = GetUserProvidedAliases("element", persister.ElementColumnNames); // NH-1612: Add aliases for all composite element properties to support access // to individual composite element properties in elements. diff --git a/src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs b/src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs index fa682449b36..3495ce76cbe 100644 --- a/src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs +++ b/src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs @@ -192,7 +192,7 @@ private void AddPersister(string alias, IDictionary propertyRe alias2EntitySuffix[alias] = suffix; if (propertyResultMap != null && propertyResultMap.Count > 0) { - entityPropertyResultMaps[alias] = propertyResultMap; + entityPropertyResultMaps[alias] = GroupComponentAliases(propertyResultMap, persister); } } @@ -203,11 +203,11 @@ private void AddCollection(string role, string alias, IDictionary 0) { - collectionPropertyResultMaps[alias] = propertyResultMap; + collectionPropertyResultMaps[alias] = FilterCollectionProperties(propertyResultMap); } - if (collectionPersister.IsOneToMany) { var persister = (ISqlLoadable)collectionPersister.ElementPersister; @@ -215,23 +215,95 @@ private void AddCollection(string role, string alias, IDictionary FilterCollectionProperties(IDictionary propertyResults) + { + if (propertyResults.Count == 0) return propertyResults; + + var result = new Dictionary(propertyResults.Count); + foreach (KeyValuePair element in propertyResults) + { + if (element.Key.IndexOf('.') < 0) + { + result.Add(element.Key, element.Value); + } + } + return result; + } + private IDictionary FilterElementProperties(IDictionary propertyResults) { const string PREFIX = "element."; + if (propertyResults.Count == 0) return propertyResults; + var result = new Dictionary(propertyResults.Count); foreach (KeyValuePair element in propertyResults) { string path = element.Key; if (path.StartsWith(PREFIX)) { - result[path.Substring(PREFIX.Length)] = element.Value; + result.Add(path.Substring(PREFIX.Length), element.Value); } } + return result; + } + + private IDictionary GroupComponentAliases(IDictionary propertyResults, ILoadable persister) + { + if (propertyResults.Count == 0) return propertyResults; + + var result = new Dictionary(propertyResults.Count); + foreach (var propertyResult in propertyResults) + { + var path = propertyResult.Key; + var dotIndex = path.IndexOf('.'); + if (dotIndex >= 0) + { + var propertyPath = path.Substring(0, dotIndex); + if (!result.ContainsKey(propertyPath)) + { + var aliases = GetUserProvidedAliases(propertyResults, propertyPath, persister.GetPropertyType(propertyPath)); + if (aliases != null) result.Add(propertyPath, aliases); + } + continue; + } + result.Add(propertyResult.Key, propertyResult.Value); + } return result; } + private string[] GetUserProvidedAliases(IDictionary propertyResults, string propertyPath, IType propertyType) + { + string[] result; + if (propertyResults.TryGetValue(propertyPath, out result)) return result; + + var aliases = new List(); + AppendUserProvidedAliases(propertyResults, propertyPath, propertyType, aliases); + return aliases.Count > 0 + ? aliases.ToArray() + : null; + } + + private void AppendUserProvidedAliases(IDictionary propertyResults, string propertyPath, IType propertyType, List result) + { + string[] aliases; + if (propertyResults.TryGetValue(propertyPath, out aliases)) + { + result.AddRange(aliases); + return; + } + + var componentType = propertyType as IAbstractComponentType; + // TODO: throw exception when no mapping is found for property name + if (componentType == null) return; + + for (int i = 0; i < componentType.PropertyNames.Length; i++) + { + AppendUserProvidedAliases(propertyResults, propertyPath + '.' + componentType.PropertyNames[i], componentType.Subtypes[i], result); + } + } + private void ProcessCollectionReturn(NativeSQLQueryCollectionReturn collectionReturn) { // we are initializing an owned collection From 55c9a2319c0727488e14570a0a29fa8c3859b647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Sat, 22 Dec 2018 17:48:30 +0100 Subject: [PATCH 3/3] Reformat new or modified code And remove some undue changes --- .../Loader/Custom/ColumnCollectionAliases.cs | 8 +- src/NHibernate/Loader/Custom/CustomLoader.cs | 2 +- .../Custom/GeneratedCollectionAliases.cs | 10 +- .../Loader/Custom/NonScalarReturn.cs | 69 +++---- .../Loader/Custom/Sql/SQLCustomQuery.cs | 8 +- .../Loader/Custom/Sql/SQLQueryContext.cs | 183 ++++++++---------- .../Loader/Custom/Sql/SQLQueryParser.cs | 7 +- 7 files changed, 127 insertions(+), 160 deletions(-) diff --git a/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs b/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs index b0cd2486556..60ac660955f 100644 --- a/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs +++ b/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs @@ -21,16 +21,16 @@ public ColumnCollectionAliases(IDictionary userProvidedAliases { this.userProvidedAliases = userProvidedAliases; - this.keyAliases = GetUserProvidedAliases("key", persister.KeyColumnNames); - this.indexAliases = GetUserProvidedAliases("index", persister.IndexColumnNames); + keyAliases = GetUserProvidedAliases("key", persister.KeyColumnNames); + indexAliases = GetUserProvidedAliases("index", persister.IndexColumnNames); // NH-1612: Add aliases for all composite element properties to support access // to individual composite element properties in elements. - this.elementAliases = persister.ElementType.IsComponentType + elementAliases = persister.ElementType.IsComponentType ? GetUserProvidedCompositeElementAliases(persister.ElementColumnNames) : GetUserProvidedAliases("element", persister.ElementColumnNames); - this.identifierAlias = GetUserProvidedAlias("id", persister.IdentifierColumnName); + identifierAlias = GetUserProvidedAlias("id", persister.IdentifierColumnName); } private string[] GetUserProvidedCompositeElementAliases(string[] defaultAliases) diff --git a/src/NHibernate/Loader/Custom/CustomLoader.cs b/src/NHibernate/Loader/Custom/CustomLoader.cs index d12e79ed0db..288d71b01ef 100644 --- a/src/NHibernate/Loader/Custom/CustomLoader.cs +++ b/src/NHibernate/Loader/Custom/CustomLoader.cs @@ -113,7 +113,7 @@ public CustomLoader(ICustomQuery customQuery, ISessionFactoryImplementor factory continue; } - + throw new HibernateException("unexpected custom query return type : " + rtn.GetType().FullName); } diff --git a/src/NHibernate/Loader/Custom/GeneratedCollectionAliases.cs b/src/NHibernate/Loader/Custom/GeneratedCollectionAliases.cs index 26161d2977c..295e0c8ca12 100644 --- a/src/NHibernate/Loader/Custom/GeneratedCollectionAliases.cs +++ b/src/NHibernate/Loader/Custom/GeneratedCollectionAliases.cs @@ -53,7 +53,8 @@ private string[] GetUserProvidedCompositeElementAliases(string[] defaultAliases) } } - if (aliases.Count > 0) return aliases.ToArray(); + if (aliases.Count > 0) + return aliases.ToArray(); } return defaultAliases; @@ -110,7 +111,12 @@ public override string ToString() private static string Join(IEnumerable aliases) { - return aliases == null ? null : StringHelper.Join(", ", aliases); + if (aliases == null) + { + return null; + } + + return StringHelper.Join(", ", aliases); } private string[] GetUserProvidedAliases(string propertyPath, string[] defaultAliases) diff --git a/src/NHibernate/Loader/Custom/NonScalarReturn.cs b/src/NHibernate/Loader/Custom/NonScalarReturn.cs index 6dcca5b3941..a603748b4ca 100644 --- a/src/NHibernate/Loader/Custom/NonScalarReturn.cs +++ b/src/NHibernate/Loader/Custom/NonScalarReturn.cs @@ -11,11 +11,7 @@ internal class NonScalarReturn : IReturn { private readonly string alias; private readonly LockMode lockMode; - private readonly NonScalarReturn owner; - private readonly ISqlLoadable entityPersister; - private readonly IEntityAliases entityAliases; - private readonly ISqlLoadableCollection collectionPersister; - private readonly ICollectionAliases collectionAliases; + private readonly ISqlLoadableCollection _collectionPersister; public NonScalarReturn(SQLQueryContext context, bool queryHasAliases, string alias, LockMode lockMode) : this(context, queryHasAliases, alias, lockMode, null) @@ -25,7 +21,7 @@ public NonScalarReturn(SQLQueryContext context, bool queryHasAliases, string ali { if (context == null) { - throw new ArgumentNullException("context"); + throw new ArgumentNullException(nameof(context)); } if (string.IsNullOrEmpty(alias)) { @@ -34,73 +30,58 @@ public NonScalarReturn(SQLQueryContext context, bool queryHasAliases, string ali this.alias = alias; this.lockMode = lockMode; - this.owner = owner; + Owner = owner; - this.collectionPersister = context.GetCollectionPersister(alias); - if (this.collectionPersister != null) + _collectionPersister = context.GetCollectionPersister(alias); + if (_collectionPersister != null) { var collectionPropertyResultMap = context.GetCollectionPropertyResultsMap(alias); - this.collectionAliases = queryHasAliases - ? new GeneratedCollectionAliases(collectionPropertyResultMap, this.collectionPersister, context.GetCollectionSuffix(alias)) - : (ICollectionAliases)new ColumnCollectionAliases(collectionPropertyResultMap, this.collectionPersister); + CollectionAliases = queryHasAliases + ? new GeneratedCollectionAliases(collectionPropertyResultMap, _collectionPersister, context.GetCollectionSuffix(alias)) + : (ICollectionAliases)new ColumnCollectionAliases(collectionPropertyResultMap, _collectionPersister); } - if (this.collectionPersister == null || this.CollectionPersister.ElementType.IsEntityType) + if (_collectionPersister == null || CollectionPersister.ElementType.IsEntityType) { - this.entityPersister = context.GetEntityPersister(alias); - if (this.entityPersister != null) + EntityPersister = context.GetEntityPersister(alias); + if (EntityPersister != null) { var entityPropertyResultMap = context.GetEntityPropertyResultsMap(alias); - this.entityAliases = queryHasAliases - ? new DefaultEntityAliases(entityPropertyResultMap, this.entityPersister, context.GetEntitySuffix(alias)) - : new ColumnEntityAliases(entityPropertyResultMap, this.entityPersister); + EntityAliases = queryHasAliases + ? new DefaultEntityAliases(entityPropertyResultMap, EntityPersister, context.GetEntitySuffix(alias)) + : new ColumnEntityAliases(entityPropertyResultMap, EntityPersister); } } } public string Alias { - get { return this.alias; } + get { return alias; } } public LockMode LockMode { - get { return this.lockMode; } + get { return lockMode; } } - public NonScalarReturn Owner - { - get { return this.owner; } - } + public NonScalarReturn Owner { get; } - public ICollectionPersister CollectionPersister - { - get { return this.collectionPersister; } - } + public ICollectionPersister CollectionPersister => _collectionPersister; - public ICollectionAliases CollectionAliases - { - get { return this.collectionAliases; } - } + public ICollectionAliases CollectionAliases { get; } - public ISqlLoadable EntityPersister - { - get { return this.entityPersister; } - } + public ISqlLoadable EntityPersister { get; } - public IEntityAliases EntityAliases - { - get { return this.entityAliases; } - } + public IEntityAliases EntityAliases { get; } public IType Type { get { - if (this.collectionPersister != null) return this.collectionPersister.CollectionType; - if (this.entityPersister != null) return this.entityPersister.EntityMetamodel.EntityType; - return null; + if (_collectionPersister != null) + return _collectionPersister.CollectionType; + return EntityPersister?.EntityMetamodel.EntityType; } } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs b/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs index a5c101c24cf..0945fa9f45a 100644 --- a/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs +++ b/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs @@ -20,15 +20,15 @@ public class SQLCustomQuery : ICustomQuery private readonly List parametersSpecifications; public SQLCustomQuery(INativeSQLQueryReturn[] queryReturns, string sqlQuery, ICollection additionalQuerySpaces, - ISessionFactoryImplementor factory) + ISessionFactoryImplementor factory) { log.Debug("starting processing of sql query [{0}]", sqlQuery); var processor = new SQLQueryContext(queryReturns, factory); var parser = new SQLQueryParser(factory, sqlQuery, processor); - this.sql = parser.Process(); - this.customQueryReturns = GenerateCustomReturns(queryReturns, parser.QueryHasAliases, processor).ToList(); - this.parametersSpecifications = parser.CollectedParametersSpecifications.ToList(); + sql = parser.Process(); + customQueryReturns = GenerateCustomReturns(queryReturns, parser.QueryHasAliases, processor).ToList(); + parametersSpecifications = parser.CollectedParametersSpecifications.ToList(); if (additionalQuerySpaces != null) { diff --git a/src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs b/src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs index 3495ce76cbe..56c62fd60a6 100644 --- a/src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs +++ b/src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs @@ -13,30 +13,28 @@ namespace NHibernate.Loader.Custom.Sql /// internal class SQLQueryContext { - private static readonly INHibernateLogger log = NHibernateLogger.For(typeof (SQLQueryContext)); + private static readonly INHibernateLogger Log = NHibernateLogger.For(typeof (SQLQueryContext)); #region Instance fields - private readonly Dictionary alias2Return = + private readonly Dictionary _alias2Return = new Dictionary(); - private readonly Dictionary alias2OwnerAlias = new Dictionary(); - - private readonly Dictionary alias2EntityPersister = new Dictionary(); - private readonly Dictionary alias2EntitySuffix = new Dictionary(); - private readonly Dictionary> entityPropertyResultMaps = + private readonly Dictionary _alias2EntityPersister = new Dictionary(); + private readonly Dictionary _alias2EntitySuffix = new Dictionary(); + private readonly Dictionary> _entityPropertyResultMaps = new Dictionary>(); - private readonly Dictionary alias2CollectionPersister = + private readonly Dictionary _alias2CollectionPersister = new Dictionary(); - private readonly Dictionary alias2CollectionSuffix = new Dictionary(); - private readonly Dictionary> collectionPropertyResultMaps = + private readonly Dictionary _alias2CollectionSuffix = new Dictionary(); + private readonly Dictionary> _collectionPropertyResultMaps = new Dictionary>(); - private readonly ISessionFactoryImplementor factory; + private readonly ISessionFactoryImplementor _factory; - private int entitySuffixSeed; - private int collectionSuffixSeed; + private int _entitySuffixSeed; + private int _collectionSuffixSeed; #endregion @@ -44,26 +42,20 @@ internal class SQLQueryContext public SQLQueryContext(INativeSQLQueryReturn[] queryReturns, ISessionFactoryImplementor factory) { - this.factory = factory; + _factory = factory; // first, break down the returns into maps keyed by alias // so that role returns can be more easily resolved to their owners - foreach (INativeSQLQueryReturn rtn in queryReturns) + foreach (var rtn in queryReturns) { - var nonScalarRtn = rtn as NativeSQLQueryNonScalarReturn; - if (nonScalarRtn != null) + if (rtn is NativeSQLQueryNonScalarReturn nonScalarRtn) { - alias2Return[nonScalarRtn.Alias] = rtn; - var joinRtn = rtn as NativeSQLQueryJoinReturn; - if (joinRtn != null) - { - alias2OwnerAlias[joinRtn.Alias] = joinRtn.OwnerAlias; - } + _alias2Return[nonScalarRtn.Alias] = rtn; } } // Now, process the returns - foreach (INativeSQLQueryReturn rtn in queryReturns) + foreach (var rtn in queryReturns) { ProcessReturn(rtn); } @@ -75,53 +67,47 @@ public SQLQueryContext(INativeSQLQueryReturn[] queryReturns, ISessionFactoryImpl public bool IsEntityAlias(string alias) { - return alias2EntityPersister.ContainsKey(alias); + return _alias2EntityPersister.ContainsKey(alias); } public ISqlLoadable GetEntityPersister(string alias) { - ISqlLoadable result; - alias2EntityPersister.TryGetValue(alias, out result); + _alias2EntityPersister.TryGetValue(alias, out var result); return result; } public string GetEntitySuffix(string alias) { - string result; - alias2EntitySuffix.TryGetValue(alias, out result); + _alias2EntitySuffix.TryGetValue(alias, out var result); return result; } public IDictionary GetEntityPropertyResultsMap(string alias) { - IDictionary result; - entityPropertyResultMaps.TryGetValue(alias, out result); + _entityPropertyResultMaps.TryGetValue(alias, out var result); return result; } public bool IsCollectionAlias(string alias) { - return alias2CollectionPersister.ContainsKey(alias); + return _alias2CollectionPersister.ContainsKey(alias); } public ISqlLoadableCollection GetCollectionPersister(string alias) { - ISqlLoadableCollection result; - alias2CollectionPersister.TryGetValue(alias, out result); + _alias2CollectionPersister.TryGetValue(alias, out var result); return result; } public string GetCollectionSuffix(string alias) { - string result; - alias2CollectionSuffix.TryGetValue(alias, out result); + _alias2CollectionSuffix.TryGetValue(alias, out var result); return result; } public IDictionary GetCollectionPropertyResultsMap(string alias) { - IDictionary result; - collectionPropertyResultMaps.TryGetValue(alias, out result); + _collectionPropertyResultMaps.TryGetValue(alias, out var result); return result; } @@ -131,9 +117,8 @@ public string GetCollectionSuffix(string alias) private ISqlLoadable GetSQLLoadable(string entityName) { - IEntityPersister persister = factory.GetEntityPersister(entityName); - var persisterAsSqlLoadable = persister as ISqlLoadable; - if (persisterAsSqlLoadable == null) + var persister = _factory.GetEntityPersister(entityName); + if (!(persister is ISqlLoadable persisterAsSqlLoadable)) { throw new MappingException("class persister is not ISqlLoadable: " + entityName); } @@ -142,85 +127,81 @@ private ISqlLoadable GetSQLLoadable(string entityName) private string GenerateEntitySuffix() { - return BasicLoader.GenerateSuffixes(entitySuffixSeed++, 1)[0]; + return BasicLoader.GenerateSuffixes(_entitySuffixSeed++, 1)[0]; } private string GenerateCollectionSuffix() { - return collectionSuffixSeed++ + "__"; + return _collectionSuffixSeed++ + "__"; } private void ProcessReturn(INativeSQLQueryReturn rtn) { - if (rtn is NativeSQLQueryScalarReturn) + switch (rtn) { - ProcessScalarReturn((NativeSQLQueryScalarReturn) rtn); - } - else if (rtn is NativeSQLQueryRootReturn) - { - ProcessRootReturn((NativeSQLQueryRootReturn) rtn); - } - else if (rtn is NativeSQLQueryCollectionReturn) - { - ProcessCollectionReturn((NativeSQLQueryCollectionReturn) rtn); - } - else - { - ProcessJoinReturn((NativeSQLQueryJoinReturn) rtn); + case NativeSQLQueryScalarReturn _: + break; + case NativeSQLQueryRootReturn root: + ProcessRootReturn(root); + break; + case NativeSQLQueryCollectionReturn collection: + ProcessCollectionReturn(collection); + break; + default: + ProcessJoinReturn((NativeSQLQueryJoinReturn) rtn); + break; } } - private void ProcessScalarReturn(NativeSQLQueryScalarReturn typeReturn) {} - private void ProcessRootReturn(NativeSQLQueryRootReturn rootReturn) { - if (alias2EntityPersister.ContainsKey(rootReturn.Alias)) + if (_alias2EntityPersister.ContainsKey(rootReturn.Alias)) { // already been processed... return; } - ISqlLoadable persister = GetSQLLoadable(rootReturn.ReturnEntityName); + var persister = GetSQLLoadable(rootReturn.ReturnEntityName); AddPersister(rootReturn.Alias, rootReturn.PropertyResultsMap, persister); } private void AddPersister(string alias, IDictionary propertyResultMap, ISqlLoadable persister) { - alias2EntityPersister[alias] = persister; - string suffix = GenerateEntitySuffix(); - log.Debug("mapping alias [" + alias + "] to entity-suffix [" + suffix + "]"); - alias2EntitySuffix[alias] = suffix; + _alias2EntityPersister[alias] = persister; + var suffix = GenerateEntitySuffix(); + Log.Debug("mapping alias [" + alias + "] to entity-suffix [" + suffix + "]"); + _alias2EntitySuffix[alias] = suffix; if (propertyResultMap != null && propertyResultMap.Count > 0) { - entityPropertyResultMaps[alias] = GroupComponentAliases(propertyResultMap, persister); + _entityPropertyResultMaps[alias] = GroupComponentAliases(propertyResultMap, persister); } } private void AddCollection(string role, string alias, IDictionary propertyResultMap) { - ISqlLoadableCollection collectionPersister = (ISqlLoadableCollection) factory.GetCollectionPersister(role); - alias2CollectionPersister[alias] = collectionPersister; - string suffix = GenerateCollectionSuffix(); - log.Debug("mapping alias [" + alias + "] to collection-suffix [" + suffix + "]"); - alias2CollectionSuffix[alias] = suffix; + var collectionPersister = (ISqlLoadableCollection) _factory.GetCollectionPersister(role); + _alias2CollectionPersister[alias] = collectionPersister; + var suffix = GenerateCollectionSuffix(); + Log.Debug("mapping alias [" + alias + "] to collection-suffix [" + suffix + "]"); + _alias2CollectionSuffix[alias] = suffix; if (propertyResultMap != null && propertyResultMap.Count > 0) { - collectionPropertyResultMaps[alias] = FilterCollectionProperties(propertyResultMap); + _collectionPropertyResultMaps[alias] = FilterCollectionProperties(propertyResultMap); } if (collectionPersister.IsOneToMany) { - var persister = (ISqlLoadable)collectionPersister.ElementPersister; + var persister = (ISqlLoadable) collectionPersister.ElementPersister; AddPersister(alias, FilterElementProperties(propertyResultMap), persister); } } - private IDictionary FilterCollectionProperties(IDictionary propertyResults) + private static IDictionary FilterCollectionProperties(IDictionary propertyResults) { if (propertyResults.Count == 0) return propertyResults; var result = new Dictionary(propertyResults.Count); - foreach (KeyValuePair element in propertyResults) + foreach (var element in propertyResults) { if (element.Key.IndexOf('.') < 0) { @@ -230,19 +211,19 @@ private void AddCollection(string role, string alias, IDictionary FilterElementProperties(IDictionary propertyResults) + private static IDictionary FilterElementProperties(IDictionary propertyResults) { - const string PREFIX = "element."; + const string prefix = "element."; if (propertyResults.Count == 0) return propertyResults; var result = new Dictionary(propertyResults.Count); - foreach (KeyValuePair element in propertyResults) + foreach (var element in propertyResults) { - string path = element.Key; - if (path.StartsWith(PREFIX)) + var path = element.Key; + if (path.StartsWith(prefix)) { - result.Add(path.Substring(PREFIX.Length), element.Value); + result.Add(path.Substring(prefix.Length), element.Value); } } return result; @@ -275,8 +256,8 @@ private void AddCollection(string role, string alias, IDictionary propertyResults, string propertyPath, IType propertyType) { - string[] result; - if (propertyResults.TryGetValue(propertyPath, out result)) return result; + if (propertyResults.TryGetValue(propertyPath, out var result)) + return result; var aliases = new List(); AppendUserProvidedAliases(propertyResults, propertyPath, propertyType, aliases); @@ -287,18 +268,17 @@ private string[] GetUserProvidedAliases(IDictionary propertyRe private void AppendUserProvidedAliases(IDictionary propertyResults, string propertyPath, IType propertyType, List result) { - string[] aliases; - if (propertyResults.TryGetValue(propertyPath, out aliases)) + if (propertyResults.TryGetValue(propertyPath, out var aliases)) { result.AddRange(aliases); return; } - var componentType = propertyType as IAbstractComponentType; // TODO: throw exception when no mapping is found for property name - if (componentType == null) return; + if (!(propertyType is IAbstractComponentType componentType)) + return; - for (int i = 0; i < componentType.PropertyNames.Length; i++) + for (var i = 0; i < componentType.PropertyNames.Length; i++) { AppendUserProvidedAliases(propertyResults, propertyPath + '.' + componentType.PropertyNames[i], componentType.Subtypes[i], result); } @@ -307,47 +287,46 @@ private void AppendUserProvidedAliases(IDictionary propertyRes private void ProcessCollectionReturn(NativeSQLQueryCollectionReturn collectionReturn) { // we are initializing an owned collection - string role = collectionReturn.OwnerEntityName + '.' + collectionReturn.OwnerProperty; + var role = collectionReturn.OwnerEntityName + '.' + collectionReturn.OwnerProperty; AddCollection(role, collectionReturn.Alias, collectionReturn.PropertyResultsMap); } private void ProcessJoinReturn(NativeSQLQueryJoinReturn fetchReturn) { - string alias = fetchReturn.Alias; - if (alias2EntityPersister.ContainsKey(alias) || alias2CollectionPersister.ContainsKey(alias)) + var alias = fetchReturn.Alias; + if (_alias2EntityPersister.ContainsKey(alias) || _alias2CollectionPersister.ContainsKey(alias)) { // already been processed... return; } - string ownerAlias = fetchReturn.OwnerAlias; + var ownerAlias = fetchReturn.OwnerAlias; // Make sure the owner alias is known... - INativeSQLQueryReturn ownerReturn; - if (!alias2Return.TryGetValue(ownerAlias, out ownerReturn)) + if (!_alias2Return.TryGetValue(ownerAlias, out var ownerReturn)) { - throw new HibernateException(string.Format("Owner alias [{0}] is unknown for alias [{1}]", ownerAlias, alias)); + throw new HibernateException($"Owner alias [{ownerAlias}] is unknown for alias [{alias}]"); } - // If this return's alias has not been processed yet, do so b4 further processing of this return - if (!alias2EntityPersister.ContainsKey(ownerAlias)) + // If this return's alias has not been processed yet, do so before further processing of this return + if (!_alias2EntityPersister.ContainsKey(ownerAlias)) { ProcessReturn(ownerReturn); } - var ownerPersister = alias2EntityPersister[ownerAlias]; + var ownerPersister = _alias2EntityPersister[ownerAlias]; var returnType = ownerPersister.GetPropertyType(fetchReturn.OwnerProperty); if (returnType.IsCollectionType) { - string role = ownerPersister.EntityName + '.' + fetchReturn.OwnerProperty; + var role = ownerPersister.EntityName + '.' + fetchReturn.OwnerProperty; AddCollection(role, alias, fetchReturn.PropertyResultsMap); } else if (returnType.IsEntityType) { - EntityType eType = (EntityType) returnType; - string returnEntityName = eType.GetAssociatedEntityName(); - ISqlLoadable persister = GetSQLLoadable(returnEntityName); + var eType = (EntityType) returnType; + var returnEntityName = eType.GetAssociatedEntityName(); + var persister = GetSQLLoadable(returnEntityName); AddPersister(alias, fetchReturn.PropertyResultsMap, persister); } } diff --git a/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs b/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs index ab43b18b2a4..ca01769f80b 100644 --- a/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs +++ b/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs @@ -42,7 +42,7 @@ public IEnumerable CollectedParametersSpecifications get { return parametersSpecifications; } } - // TODO: should "record" how many properties we have refered to - and if we + // TODO: should "record" how many properties we have referred to - and if we // don't get'em'all we throw an exception! Way better than trial and error ;) private string SubstituteBrackets() { @@ -206,8 +206,9 @@ private string ResolveProperties(string aliasName, string propertyName) if (columnAliases == null || columnAliases.Length == 0) { - throw new QueryException("No column name found for property [" + propertyName + "] for alias [" + aliasName + "]", - originalQueryString); + throw new QueryException( + "No column name found for property [" + propertyName + "] for alias [" + aliasName + "]", + originalQueryString); } if (columnAliases.Length != 1) {