Skip to content

Commit d7e2295

Browse files
David EllingsworthDavid Ellingsworth
authored andcommitted
Refactor JoinWalker and related subclasses to traverse the entity graph level by level.
1 parent d2599c6 commit d7e2295

File tree

3 files changed

+128
-21
lines changed

3 files changed

+128
-21
lines changed

src/NHibernate/Loader/AbstractEntityJoinWalker.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public AbstractEntityJoinWalker(string rootSqlAlias, IOuterJoinLoadable persiste
3131
protected virtual void InitAll(SqlString whereString, SqlString orderByString, LockMode lockMode)
3232
{
3333
AddAssociations();
34+
WalkTree();
3435
IList<OuterJoinableAssociation> allAssociations = new List<OuterJoinableAssociation>(associations);
3536
var rootAssociation = CreateRootAssociation();
3637
allAssociations.Add(rootAssociation);

src/NHibernate/Loader/Criteria/CriteriaJoinWalker.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,16 @@ protected override void AddAssociations()
8686
AddExplicitEntityJoinAssociation(persister, tableAlias, translator.GetJoinType(criteriaPath, criteriaPathAlias), criteriaPath, criteriaPathAlias);
8787
IncludeInResultIfNeeded(persister, entityJoinInfo.Criteria, tableAlias, criteriaPath);
8888
//collect mapped associations for entity join
89-
WalkEntityTree(persister, tableAlias, criteriaPath, 1);
89+
joinQueue.Enqueue(
90+
new EntityQueueEntry()
91+
{
92+
Persister = persister,
93+
Alias = tableAlias,
94+
Path = criteriaPath,
95+
}
96+
);
9097
}
98+
joinQueue.Enqueue(null);
9199
}
92100

93101
protected override void WalkEntityTree(IOuterJoinLoadable persister, string alias, string path, int currentDepth)

src/NHibernate/Loader/JoinWalker.cs

Lines changed: 118 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ namespace NHibernate.Loader
1515
public class JoinWalker
1616
{
1717
private readonly ISessionFactoryImplementor factory;
18+
protected Queue<QueueEntry> joinQueue = new Queue<QueueEntry>();
19+
private int depth;
1820
protected readonly IList<OuterJoinableAssociation> associations = new List<OuterJoinableAssociation>();
1921
private readonly HashSet<AssociationKey> visitedAssociationKeys = new HashSet<AssociationKey>();
2022
private readonly IDictionary<string, IFilter> enabledFilters;
@@ -188,17 +190,28 @@ private void AddAssociationToJoinTree(IAssociationType type, string[] aliasedLhs
188190
assoc.ValidateJoin(path);
189191
AddAssociation(assoc);
190192

191-
int nextDepth = currentDepth + 1;
192-
193-
if (qc == null)
193+
if (qc != null)
194194
{
195-
IOuterJoinLoadable pjl = joinable as IOuterJoinLoadable;
196-
if (pjl != null)
197-
WalkEntityTree(pjl, subalias, path, nextDepth);
195+
joinQueue.Enqueue(
196+
new CollectionQueueEntry()
197+
{
198+
Persister = qc,
199+
Alias = subalias,
200+
Path = path,
201+
PathAlias = pathAlias,
202+
}
203+
);
198204
}
199-
else
205+
else if (joinable is IOuterJoinLoadable jl)
200206
{
201-
WalkCollectionTree(qc, subalias, path, pathAlias, nextDepth);
207+
joinQueue.Enqueue(
208+
new EntityQueueEntry()
209+
{
210+
Persister = jl,
211+
Alias = subalias,
212+
Path = path,
213+
}
214+
);
202215
}
203216
}
204217

@@ -299,16 +312,55 @@ private void AddAssociation(OuterJoinableAssociation association)
299312
/// </summary>
300313
protected void WalkEntityTree(IOuterJoinLoadable persister, string alias)
301314
{
302-
WalkEntityTree(persister, alias, string.Empty, 0);
315+
joinQueue.Enqueue(
316+
new EntityQueueEntry()
317+
{
318+
Persister = persister,
319+
Alias = alias,
320+
}
321+
);
322+
323+
WalkTree();
303324
}
304325

305326
/// <summary>
306327
/// For a collection role, return a list of associations to be fetched by outerjoin
307328
/// </summary>
308329
protected void WalkCollectionTree(IQueryableCollection persister, string alias)
309330
{
310-
WalkCollectionTree(persister, alias, string.Empty, string.Empty, 0);
311-
//TODO: when this is the entry point, we should use an INNER_JOIN for fetching the many-to-many elements!
331+
joinQueue.Enqueue(
332+
new CollectionQueueEntry()
333+
{
334+
Persister = persister,
335+
Alias = alias,
336+
}
337+
);
338+
339+
WalkTree();
340+
}
341+
342+
protected void WalkTree()
343+
{
344+
while(joinQueue.Count > 0)
345+
{
346+
QueueEntry entry = joinQueue.Dequeue();
347+
348+
switch(entry)
349+
{
350+
case CollectionQueueEntry ce:
351+
WalkCollectionTree(ce.Persister, ce.Alias, ce.Path, ce.PathAlias, depth);
352+
break;
353+
case PropertyQueueEntry pe:
354+
WalkPropertyTree(pe.Persister, pe.Type, pe.Index, pe.Alias, pe.Path, depth);
355+
break;
356+
case EntityQueueEntry eqe:
357+
WalkEntityTree(eqe.Persister, eqe.Alias, eqe.Path, depth);
358+
break;
359+
default:
360+
depth++;
361+
break;
362+
}
363+
}
312364
}
313365

314366
/// <summary>
@@ -442,21 +494,42 @@ private void WalkEntityAssociationTree(IAssociationType associationType, IOuterJ
442494
protected virtual void WalkEntityTree(IOuterJoinLoadable persister, string alias, string path, int currentDepth)
443495
{
444496
int n = persister.CountSubclassProperties();
497+
445498
for (int i = 0; i < n; i++)
446499
{
447500
IType type = persister.GetSubclassPropertyType(i);
448-
ILhsAssociationTypeSqlInfo associationTypeSQLInfo = JoinHelper.GetLhsSqlInfo(alias, i, persister, Factory);
449-
if (type.IsAssociationType)
450-
{
451-
WalkEntityAssociationTree((IAssociationType) type, persister, i, alias, path,
452-
persister.IsSubclassPropertyNullable(i), currentDepth, associationTypeSQLInfo);
453-
}
454-
else if (type.IsComponentType)
501+
502+
if (type.IsAssociationType || type.IsComponentType)
455503
{
456-
WalkComponentTree((IAbstractComponentType) type, 0, alias, SubPath(path, persister.GetSubclassPropertyName(i)),
457-
currentDepth, associationTypeSQLInfo);
504+
joinQueue.Enqueue(
505+
new PropertyQueueEntry()
506+
{
507+
Persister = persister,
508+
Alias = alias,
509+
Path = path,
510+
Index = i,
511+
Type = type,
512+
}
513+
);
458514
}
459515
}
516+
joinQueue.Enqueue(null);
517+
}
518+
519+
protected void WalkPropertyTree(IOuterJoinLoadable persister, IType type, int index, string alias, string path, int currentDepth)
520+
{
521+
ILhsAssociationTypeSqlInfo associationTypeSQLInfo = JoinHelper.GetLhsSqlInfo(alias, index, persister, Factory);
522+
523+
if (type.IsAssociationType)
524+
{
525+
WalkEntityAssociationTree((IAssociationType) type, persister, index, alias, path,
526+
persister.IsSubclassPropertyNullable(index), currentDepth, associationTypeSQLInfo);
527+
}
528+
else if (type.IsComponentType)
529+
{
530+
WalkComponentTree((IAbstractComponentType) type, 0, alias, SubPath(path, persister.GetSubclassPropertyName(index)),
531+
currentDepth, associationTypeSQLInfo);
532+
}
460533
}
461534

462535
/// <summary>
@@ -1201,5 +1274,30 @@ protected static string GetSelectFragment(OuterJoinableAssociation join, string
12011274
{
12021275
return join.GetSelectFragment(entitySuffix, collectionSuffix, next);
12031276
}
1277+
1278+
protected abstract class QueueEntry { }
1279+
1280+
protected abstract class QueueEntry<T> : QueueEntry where T : IJoinable
1281+
{
1282+
public string Alias { get; set; }
1283+
public T Persister { get; set; }
1284+
}
1285+
1286+
protected class EntityQueueEntry<T> : QueueEntry<T> where T : IJoinable
1287+
{
1288+
public string Path { get; set; }
1289+
}
1290+
protected class EntityQueueEntry : EntityQueueEntry<IOuterJoinLoadable> {}
1291+
1292+
protected class CollectionQueueEntry : EntityQueueEntry<IQueryableCollection>
1293+
{
1294+
public string PathAlias { get; set; }
1295+
}
1296+
1297+
protected class PropertyQueueEntry : EntityQueueEntry
1298+
{
1299+
public int Index { get; set; }
1300+
public IType Type { get; set; }
1301+
}
12041302
}
12051303
}

0 commit comments

Comments
 (0)