Skip to content

Commit a04f3bb

Browse files
committed
Fix using HQL for NH-3848
1 parent 959a2a6 commit a04f3bb

File tree

7 files changed

+101
-25
lines changed

7 files changed

+101
-25
lines changed

src/NHibernate/Engine/Loading/CollectionLoadContext.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ public IPersistentCollection GetLoadingCollection(ICollectionPersister persister
144144
/// complete.
145145
/// </summary>
146146
/// <param name="persister">The persister for which to complete loading. </param>
147-
public void EndLoadingCollections(ICollectionPersister persister)
147+
/// <param name="canAddCollectionsToCache">Indicates if collcetion can be put in cache</param>
148+
public void EndLoadingCollections(ICollectionPersister persister, bool canAddCollectionsToCache)
148149
{
149150
if (!loadContexts.HasLoadingCollectionEntries && (localLoadingCollectionKeys.Count == 0))
150151
{
@@ -188,7 +189,7 @@ public void EndLoadingCollections(ICollectionPersister persister)
188189
}
189190
localLoadingCollectionKeys.ExceptWith(toRemove);
190191

191-
EndLoadingCollections(persister, matches);
192+
EndLoadingCollections(persister, matches, canAddCollectionsToCache);
192193
if ((localLoadingCollectionKeys.Count == 0))
193194
{
194195
// todo : hack!!!
@@ -201,7 +202,7 @@ public void EndLoadingCollections(ICollectionPersister persister)
201202
}
202203
}
203204

204-
private void EndLoadingCollections(ICollectionPersister persister, IList<LoadingCollectionEntry> matchedCollectionEntries)
205+
private void EndLoadingCollections(ICollectionPersister persister, IList<LoadingCollectionEntry> matchedCollectionEntries, bool canAddCollectionsToCache)
205206
{
206207
if (matchedCollectionEntries == null || matchedCollectionEntries.Count == 0)
207208
{
@@ -220,7 +221,7 @@ private void EndLoadingCollections(ICollectionPersister persister, IList<Loading
220221

221222
for (int i = 0; i < count; i++)
222223
{
223-
EndLoadingCollection(matchedCollectionEntries[i], persister);
224+
EndLoadingCollection(matchedCollectionEntries[i], persister, canAddCollectionsToCache);
224225
}
225226

226227
if (log.IsDebugEnabled)
@@ -229,7 +230,7 @@ private void EndLoadingCollections(ICollectionPersister persister, IList<Loading
229230
}
230231
}
231232

232-
private void EndLoadingCollection(LoadingCollectionEntry lce, ICollectionPersister persister)
233+
private void EndLoadingCollection(LoadingCollectionEntry lce, ICollectionPersister persister, bool canAddCollectionsToCache)
233234
{
234235
if (log.IsDebugEnabled)
235236
{
@@ -262,7 +263,7 @@ private void EndLoadingCollection(LoadingCollectionEntry lce, ICollectionPersist
262263
ce.PostInitialize(lce.Collection);
263264
}
264265

265-
bool addToCache = hasNoQueuedAdds && persister.HasCache &&
266+
bool addToCache = canAddCollectionsToCache && hasNoQueuedAdds && persister.HasCache &&
266267
((session.CacheMode & CacheMode.Put) == CacheMode.Put) && !ce.IsDoremove; // and this is not a forced initialization during flush
267268

268269
if (addToCache)

src/NHibernate/Engine/QueryParameters.cs

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

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

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

5050
public QueryParameters(IDictionary<string, TypedValue> namedParameters, IDictionary<string, LockMode> lockModes, RowSelection rowSelection, bool isReadOnlyInitialized,
51-
bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer)
51+
bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer, bool canAddCollectionsToCache)
5252
: this(
5353
TypeHelper.EmptyTypeArray, ArrayHelper.EmptyObjectArray, namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null,
54-
transformer)
54+
transformer, canAddCollectionsToCache)
5555
{
5656
// used by CriteriaTranslator
5757
NaturalKeyLookup = isLookupByNaturalKey;
5858
}
5959

6060
public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary<string, TypedValue> namedParameters,
6161
IDictionary<string, LockMode> lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion,
62-
string comment, object[] collectionKeys, IResultTransformer transformer)
62+
string comment, object[] collectionKeys, IResultTransformer transformer, bool canAddCollectionsToCache)
6363
{
6464
PositionalParameterTypes = positionalParameterTypes ?? new IType[0];
6565
PositionalParameterValues = positionalParameterValues ?? new object[0];
@@ -73,14 +73,15 @@ public QueryParameters(IType[] positionalParameterTypes, object[] positionalPara
7373
IsReadOnlyInitialized = isReadOnlyInitialized;
7474
this.readOnly = readOnly;
7575
ResultTransformer = transformer;
76+
CanAddCollectionsToCache = canAddCollectionsToCache;
7677
}
7778

7879
public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary<string, TypedValue> namedParameters,
7980
IDictionary<string, LockMode> lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion,
8081
string comment, object[] collectionKeys, object optionalObject, string optionalEntityName, object optionalId, IResultTransformer transformer)
8182
: this(
8283
positionalParameterTypes, positionalParameterValues, namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, collectionKeys,
83-
transformer)
84+
transformer, true)
8485
{
8586
OptionalEntityName = optionalEntityName;
8687
OptionalId = optionalId;
@@ -138,6 +139,11 @@ public bool HasRowSelection
138139

139140
public bool Callable { get; set; }
140141

142+
/// <summary>
143+
/// Indicates if we can add loaded children collections to second lvl cache.
144+
/// </summary>
145+
public bool CanAddCollectionsToCache { get; set; }
146+
141147
public bool ReadOnly
142148
{
143149
get

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 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,12 +287,32 @@ 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;
289291
}
290292
}
291293

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

437505
public class HqlParseEngine

src/NHibernate/Impl/MultiCriteriaImpl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ private void GetResultsFromDatabase(IList results)
266266
for (int i = 0; i < loaders.Count; i++)
267267
{
268268
CriteriaLoader loader = loaders[i];
269-
loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, session.DefaultReadOnly);
269+
loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, session.DefaultReadOnly, parameters[i].CanAddCollectionsToCache);
270270

271271
if (createSubselects[i])
272272
{

src/NHibernate/Impl/MultiQueryImpl.cs

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

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

602602
if (createSubselects[i])
603603
{

src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,8 @@ public QueryParameters GetQueryParameters()
149149
rootCriteria.CacheRegion,
150150
rootCriteria.Comment,
151151
rootCriteria.LookupByNaturalKey,
152-
rootCriteria.ResultTransformer);
152+
rootCriteria.ResultTransformer,
153+
true);
153154
}
154155

155156
public SqlString GetGroupBy()

src/NHibernate/Loader/Loader.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ protected object LoadSingleRow(IDataReader resultSet, ISessionImplementor sessio
299299
queryParameters.NamedParameters);
300300
}
301301

302-
InitializeEntitiesAndCollections(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session));
302+
InitializeEntitiesAndCollections(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session), queryParameters.CanAddCollectionsToCache);
303303
session.PersistenceContext.InitializeNonLazyCollections();
304304
return result;
305305
}
@@ -493,7 +493,7 @@ private IList DoQuery(ISessionImplementor session, QueryParameters queryParamete
493493
session.Batcher.CloseCommand(st, rs);
494494
}
495495

496-
InitializeEntitiesAndCollections(hydratedObjects, rs, session, queryParameters.IsReadOnly(session));
496+
InitializeEntitiesAndCollections(hydratedObjects, rs, session, queryParameters.IsReadOnly(session), queryParameters.CanAddCollectionsToCache);
497497

498498
if (createSubselects)
499499
{
@@ -575,7 +575,7 @@ private IEnumerable<SubselectFetch> CreateSubselects(IList<EntityKey[]> keys, Qu
575575
}
576576
}
577577

578-
internal void InitializeEntitiesAndCollections(IList hydratedObjects, object resultSetId, ISessionImplementor session, bool readOnly)
578+
internal void InitializeEntitiesAndCollections(IList hydratedObjects, object resultSetId, ISessionImplementor session, bool readOnly, bool canAddCollectionsToCache)
579579
{
580580
ICollectionPersister[] collectionPersisters = CollectionPersisters;
581581
if (collectionPersisters != null)
@@ -589,7 +589,7 @@ internal void InitializeEntitiesAndCollections(IList hydratedObjects, object res
589589
//during loading
590590
//TODO: or we could do this polymorphically, and have two
591591
// different operations implemented differently for arrays
592-
EndCollectionLoad(resultSetId, session, collectionPersisters[i]);
592+
EndCollectionLoad(resultSetId, session, collectionPersisters[i], canAddCollectionsToCache);
593593
}
594594
}
595595
}
@@ -633,17 +633,17 @@ internal void InitializeEntitiesAndCollections(IList hydratedObjects, object res
633633
//the entities, since we might call hashCode() on the elements
634634
//TODO: or we could do this polymorphically, and have two
635635
// different operations implemented differently for arrays
636-
EndCollectionLoad(resultSetId, session, collectionPersisters[i]);
636+
EndCollectionLoad(resultSetId, session, collectionPersisters[i], canAddCollectionsToCache);
637637
}
638638
}
639639
}
640640
}
641641

642-
private static void EndCollectionLoad(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister)
642+
private static void EndCollectionLoad(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister, bool canAddCollectionsToCache)
643643
{
644644
//this is a query and we are loading multiple instances of the same collection role
645645
session.PersistenceContext.LoadContexts.GetCollectionLoadContext((IDataReader)resultSetId).EndLoadingCollections(
646-
collectionPersister);
646+
collectionPersister, canAddCollectionsToCache);
647647
}
648648

649649

0 commit comments

Comments
 (0)