Skip to content

Commit 86b51e7

Browse files
itmaginationhazzik
authored andcommitted
Fix using HQL for NH-3848
1 parent 6a70bfc commit 86b51e7

File tree

6 files changed

+98
-23
lines changed

6 files changed

+98
-23
lines changed

src/NHibernate/Engine/QueryParameters.cs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,28 +37,28 @@ public QueryParameters(IType[] positionalParameterTypes, object[] postionalParam
3737
: this(positionalParameterTypes, postionalParameterValues, null, collectionKeys) {}
3838

3939
public QueryParameters(IType[] positionalParameterTypes, object[] postionalParameterValues, IDictionary<string, TypedValue> namedParameters, object[] collectionKeys)
40-
: this(positionalParameterTypes, postionalParameterValues, namedParameters, null, null, false, false, false, null, null, collectionKeys, null) {}
40+
: this(positionalParameterTypes, postionalParameterValues, namedParameters, null, null, false, false, false, null, null, collectionKeys, null, true) {}
4141

4242
public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary<string, LockMode> lockModes, RowSelection rowSelection,
43-
bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer)
44-
: this(positionalParameterTypes, positionalParameterValues, null, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, transformer)
43+
bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer)
44+
: this(positionalParameterTypes, positionalParameterValues, null, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, transformer, true)
4545
{
4646
NaturalKeyLookup = isLookupByNaturalKey;
4747
}
4848

4949
public QueryParameters(IDictionary<string, TypedValue> namedParameters, IDictionary<string, LockMode> lockModes, RowSelection rowSelection, bool isReadOnlyInitialized,
50-
bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer)
50+
bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer, bool canAddCollectionsToCache)
5151
: this(
5252
TypeHelper.EmptyTypeArray, Array.Empty<object>(), namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null,
53-
transformer)
53+
transformer, canAddCollectionsToCache)
5454
{
5555
// used by CriteriaTranslator
5656
NaturalKeyLookup = isLookupByNaturalKey;
5757
}
5858

5959
public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary<string, TypedValue> namedParameters,
60-
IDictionary<string, LockMode> lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion,
61-
string comment, object[] collectionKeys, IResultTransformer transformer)
60+
IDictionary<string, LockMode> lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion,
61+
string comment, object[] collectionKeys, IResultTransformer transformer, bool canAddCollectionsToCache)
6262
{
6363
PositionalParameterTypes = positionalParameterTypes ?? Array.Empty<IType>();
6464
PositionalParameterValues = positionalParameterValues ?? Array.Empty<object>();
@@ -72,14 +72,15 @@ public QueryParameters(IType[] positionalParameterTypes, object[] positionalPara
7272
IsReadOnlyInitialized = isReadOnlyInitialized;
7373
this.readOnly = readOnly;
7474
ResultTransformer = transformer;
75+
CanAddCollectionsToCache = canAddCollectionsToCache;
7576
}
7677

7778
public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary<string, TypedValue> namedParameters,
78-
IDictionary<string, LockMode> lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion,
79-
string comment, object[] collectionKeys, object optionalObject, string optionalEntityName, object optionalId, IResultTransformer transformer)
79+
IDictionary<string, LockMode> lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion,
80+
string comment, object[] collectionKeys, object optionalObject, string optionalEntityName, object optionalId, IResultTransformer transformer)
8081
: this(
8182
positionalParameterTypes, positionalParameterValues, namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, collectionKeys,
82-
transformer)
83+
transformer, true)
8384
{
8485
OptionalEntityName = optionalEntityName;
8586
OptionalId = optionalId;
@@ -139,6 +140,11 @@ public bool HasRowSelection
139140

140141
public bool Callable { get; set; }
141142

143+
/// <summary>
144+
/// Indicates if we can add loaded children collections to second lvl cache.
145+
/// </summary>
146+
public bool CanAddCollectionsToCache { get; set; }
147+
142148
public bool ReadOnly
143149
{
144150
get
@@ -196,7 +202,7 @@ public void ValidateParameters()
196202
if (typesLength != valuesLength)
197203
{
198204
throw new QueryException("Number of positional parameter types (" + typesLength
199-
+ ") does not match number of positional parameter values (" + valuesLength + ")");
205+
+ ") does not match number of positional parameter values (" + valuesLength + ")");
200206
}
201207
}
202208

src/NHibernate/Hql/Ast/ANTLR/QueryTranslatorImpl.cs

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public partial class QueryTranslatorImpl : IFilterTranslator
3737
private IStatement _sqlAst;
3838
private IDictionary<string, string> _tokenReplacements;
3939
private HqlSqlGenerator _generator;
40+
private IList<IASTNode> collectionFetches;
4041

4142
/// <summary>
4243
/// Creates a new AST-based query translator.
@@ -107,6 +108,9 @@ public IList List(ISessionImplementor session, QueryParameters queryParameters)
107108
queryParametersToUse = queryParameters;
108109
}
109110

111+
if (session.CacheMode.HasFlag(CacheMode.Put) && CanAddFetchedCollectionToCache)
112+
queryParametersToUse.CanAddCollectionsToCache = false;
113+
110114
IList results = _queryLoader.List(session, queryParametersToUse);
111115

112116
if ( needsDistincting )
@@ -283,9 +287,29 @@ public bool ContainsCollectionFetches
283287
{
284288
get
285289
{
286-
ErrorIfDML();
287-
IList<IASTNode> collectionFetches = ((QueryNode)_sqlAst).FromClause.GetCollectionFetches();
288-
return collectionFetches != null && collectionFetches.Count > 0;
290+
return CollectionFetches.Count > 0;
291+
}
292+
}
293+
294+
public bool CanAddFetchedCollectionToCache
295+
{
296+
get
297+
{
298+
foreach (var collectionFetch in CollectionFetches)
299+
{
300+
var fromElement = (collectionFetch as FromElement);
301+
var hasCache = fromElement != null &&
302+
fromElement.QueryableCollection != null &&
303+
fromElement.QueryableCollection.HasCache;
304+
305+
if (!hasCache)
306+
continue;
307+
308+
if (ContainsRestrictionOnTable(fromElement.TableAlias))
309+
return true;
310+
}
311+
312+
return false;
289313
}
290314
}
291315

@@ -430,6 +454,50 @@ private void ErrorIfDML()
430454
throw new QueryExecutionRequestException("Not supported for DML operations", _queryIdentifier);
431455
}
432456
}
457+
458+
private bool ContainsRestrictionOnTable(string tableAlias)
459+
{
460+
var whereClause = ((QueryNode)_sqlAst).WhereClause;
461+
if (whereClause == null)
462+
return false;
463+
464+
var tableAliasWithDot = tableAlias + ".";
465+
var stack = new Stack<IASTNode>();
466+
for (var i = 0; i < whereClause.ChildCount; i++)
467+
{
468+
stack.Push(whereClause.GetChild(i));
469+
while (stack.Count != 0)
470+
{
471+
var child = stack.Pop();
472+
if (child.ChildCount > 0)
473+
{
474+
//We're iterating from count to 0 because it is more common to put restricting column as a left operand.
475+
//e.g WHERE fetchedCollectionAlias.Column = 1. Now we put on stack first '1' and then 'fetchedCollectionAlias.Column'
476+
//so 'fetchedCollectionAlias.Column' will be on top.
477+
for (var j = child.ChildCount - 1; j >= 0; j--)
478+
stack.Push(child.GetChild(j));
479+
}
480+
else if (child.Text.StartsWith(tableAliasWithDot, StringComparison.Ordinal))
481+
return true;
482+
}
483+
}
484+
485+
return false;
486+
}
487+
488+
private IList<IASTNode> CollectionFetches
489+
{
490+
get
491+
{
492+
if (collectionFetches == null)
493+
{
494+
ErrorIfDML();
495+
collectionFetches = ((QueryNode)_sqlAst).FromClause.GetCollectionFetches() ?? new List<IASTNode>();
496+
}
497+
498+
return collectionFetches;
499+
}
500+
}
433501
}
434502

435503
public class HqlParseEngine

src/NHibernate/Impl/MultiCriteriaImpl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ private void GetResultsFromDatabase(IList results)
273273
for (int i = 0; i < loaders.Count; i++)
274274
{
275275
CriteriaLoader loader = loaders[i];
276-
loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, session.DefaultReadOnly);
276+
loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, session.DefaultReadOnly, parameters[i].CanAddCollectionsToCache);
277277

278278
if (createSubselects[i])
279279
{

src/NHibernate/Impl/MultiQueryImpl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ protected List<object> DoList()
616616
ITranslator translator = translators[i];
617617
QueryParameters parameter = parameters[i];
618618

619-
translator.Loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, false);
619+
translator.Loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, false, parameters[i].CanAddCollectionsToCache);
620620

621621
if (createSubselects[i])
622622
{

src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,8 @@ public QueryParameters GetQueryParameters()
168168
rootCriteria.CacheRegion,
169169
rootCriteria.Comment,
170170
rootCriteria.LookupByNaturalKey,
171-
rootCriteria.ResultTransformer)
171+
rootCriteria.ResultTransformer,
172+
true)
172173
{
173174
CacheMode = rootCriteria.CacheMode
174175
};

src/NHibernate/Loader/Loader.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ protected object LoadSingleRow(DbDataReader resultSet, ISessionImplementor sessi
315315
queryParameters.NamedParameters);
316316
}
317317

318-
InitializeEntitiesAndCollections(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session));
318+
InitializeEntitiesAndCollections(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session), queryParameters.CanAddCollectionsToCache);
319319
session.PersistenceContext.InitializeNonLazyCollections();
320320
return result;
321321
}
@@ -517,7 +517,7 @@ private IList DoQuery(ISessionImplementor session, QueryParameters queryParamete
517517
session.Batcher.CloseCommand(st, rs);
518518
}
519519

520-
InitializeEntitiesAndCollections(hydratedObjects, rs, session, queryParameters.IsReadOnly(session));
520+
InitializeEntitiesAndCollections(hydratedObjects, rs, session, queryParameters.IsReadOnly(session), queryParameters.CanAddCollectionsToCache);
521521

522522
if (createSubselects)
523523
{
@@ -600,7 +600,7 @@ private IEnumerable<SubselectFetch> CreateSubselects(IList<EntityKey[]> keys, Qu
600600
}
601601

602602
internal void InitializeEntitiesAndCollections(
603-
IList hydratedObjects, DbDataReader reader, ISessionImplementor session, bool readOnly,
603+
IList hydratedObjects, DbDataReader reader, ISessionImplementor session, bool readOnly, bool canAddCollectionsToCache,
604604
CacheBatcher cacheBatcher = null)
605605
{
606606
ICollectionPersister[] collectionPersisters = CollectionPersisters;
@@ -615,7 +615,7 @@ internal void InitializeEntitiesAndCollections(
615615
//during loading
616616
//TODO: or we could do this polymorphically, and have two
617617
// different operations implemented differently for arrays
618-
EndCollectionLoad(reader, session, collectionPersister);
618+
EndCollectionLoad(reader, session, collectionPersister, canAddCollectionsToCache);
619619
}
620620
}
621621
}
@@ -666,13 +666,13 @@ internal void InitializeEntitiesAndCollections(
666666
//the entities, since we might call hashCode() on the elements
667667
//TODO: or we could do this polymorphically, and have two
668668
// different operations implemented differently for arrays
669-
EndCollectionLoad(reader, session, collectionPersister);
669+
EndCollectionLoad(reader, session, collectionPersister, canAddCollectionsToCache);
670670
}
671671
}
672672
}
673673
}
674674

675-
private void EndCollectionLoad(DbDataReader reader, ISessionImplementor session, ICollectionPersister collectionPersister)
675+
private void EndCollectionLoad(DbDataReader reader, ISessionImplementor session, ICollectionPersister collectionPersister, bool canAddCollectionsToCache)
676676
{
677677
//this is a query and we are loading multiple instances of the same collection role
678678
session.PersistenceContext.LoadContexts.GetCollectionLoadContext(reader).EndLoadingCollections(

0 commit comments

Comments
 (0)