Skip to content

Commit 56c1bb8

Browse files
David EllingsworthDavid Ellingsworth
David Ellingsworth
authored and
David Ellingsworth
committed
GH2201: Refactor JoinWalker and related subclasses to traverse the entity graph level by level.
1 parent c6c8c59 commit 56c1bb8

File tree

3 files changed

+138
-31
lines changed

3 files changed

+138
-31
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: 128 additions & 30 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;
@@ -180,29 +182,40 @@ private void AddAssociationToJoinTree(IAssociationType type, string[] aliasedLhs
180182
subalias,
181183
joinType,
182184
//for many-to-many with clause is applied with OuterJoinableAssociation created for entity persister so simply skip it here
183-
qc?.IsManyToMany == true ? null :GetWithClause(path, pathAlias),
185+
qc?.IsManyToMany == true ? null : GetWithClause(path, pathAlias),
184186
Factory,
185187
enabledFilters,
186188
GetSelectMode(path)),
187189
path);
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

205-
protected virtual SelectMode GetSelectMode(string path)
218+
protected virtual SelectMode GetSelectMode(string path)
206219
{
207220
return SelectMode.Undefined;
208221
}
@@ -211,7 +224,7 @@ protected virtual ISet<string> GetEntityFetchLazyProperties(string path)
211224
{
212225
return null;
213226
}
214-
227+
215228
private struct DependentAlias2
216229
{
217230
public DependentAlias2(string alias, ICollection<string> dependsOn)
@@ -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>
@@ -318,7 +370,7 @@ private void WalkCollectionTree(IQueryableCollection persister, string alias, st
318370
{
319371
if (persister.IsOneToMany)
320372
{
321-
WalkEntityTree((IOuterJoinLoadable)persister.ElementPersister, alias, path, currentDepth);
373+
WalkEntityTree((IOuterJoinLoadable) persister.ElementPersister, alias, path, currentDepth);
322374
}
323375
else
324376
{
@@ -388,7 +440,7 @@ internal void AddExplicitEntityJoinAssociation(
388440
GetWithClause(path, pathAlias),
389441
Factory,
390442
enabledFilters,
391-
GetSelectMode(path)) {ForceFilter = true},
443+
GetSelectMode(path)) { ForceFilter = true },
392444
path);
393445
AddAssociation(assoc);
394446
}
@@ -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>
@@ -680,7 +753,7 @@ protected bool IsJoinedFetchEnabledInMapping(FetchMode config, IAssociationType
680753
{
681754
//TODO: look at the owning property and check that it
682755
// isn't lazy (by instrumentation)
683-
EntityType entityType = (EntityType)type;
756+
EntityType entityType = (EntityType) type;
684757
IEntityPersister persister = factory.GetEntityPersister(entityType.GetAssociatedEntityName());
685758
return !persister.HasProxy;
686759
}
@@ -849,7 +922,7 @@ protected JoinFragment MergeOuterJoins(IList<OuterJoinableAssociation> associati
849922
}
850923
}
851924

852-
if (TableGroupJoinHelper.ProcessAsTableGroupJoin(new[] {oj}, new[] {oj.On, filter}, true, outerjoin, alias => true, factory))
925+
if (TableGroupJoinHelper.ProcessAsTableGroupJoin(new[] { oj }, new[] { oj.On, filter }, true, outerjoin, alias => true, factory))
853926
continue;
854927

855928
oj.AddJoins(outerjoin);
@@ -954,7 +1027,7 @@ protected SqlString OrderBy(IList<OuterJoinableAssociation> associations)
9541027
}
9551028

9561029
if (buf.Count > 0) {
957-
buf.RemoveAt(buf.Count-1);
1030+
buf.RemoveAt(buf.Count - 1);
9581031
}
9591032

9601033
return buf.ToSqlString();
@@ -980,7 +1053,7 @@ protected SqlStringBuilder WhereString(string alias, string[] columnNames, int b
9801053
var qualifiedName = !string.IsNullOrEmpty(tableAlias)
9811054
? tableAlias + StringHelper.Dot + columnName
9821055
: columnName;
983-
1056+
9841057
var whereString = new SqlStringBuilder(batchSize * 5);
9851058
whereString.Add(qualifiedName);
9861059
if (batchSize == 1)
@@ -1101,7 +1174,7 @@ protected void InitPersisters(IList<OuterJoinableAssociation> associations, Lock
11011174
}
11021175
else
11031176
{
1104-
IQueryableCollection collPersister = (IQueryableCollection)oj.Joinable;
1177+
IQueryableCollection collPersister = (IQueryableCollection) oj.Joinable;
11051178
if (oj.ShouldFetchCollectionPersister())
11061179
{
11071180
//it must be a collection fetch
@@ -1186,5 +1259,30 @@ protected static string GetSelectFragment(OuterJoinableAssociation join, string
11861259
{
11871260
return join.GetSelectFragment(entitySuffix, collectionSuffix, next);
11881261
}
1262+
1263+
protected abstract class QueueEntry { }
1264+
1265+
protected abstract class QueueEntry<T> : QueueEntry where T : IJoinable
1266+
{
1267+
public string Alias { get; set; }
1268+
public T Persister { get; set; }
1269+
}
1270+
1271+
protected class EntityQueueEntry<T> : QueueEntry<T> where T : IJoinable
1272+
{
1273+
public string Path { get; set; }
1274+
}
1275+
protected class EntityQueueEntry : EntityQueueEntry<IOuterJoinLoadable> {}
1276+
1277+
protected class CollectionQueueEntry : EntityQueueEntry<IQueryableCollection>
1278+
{
1279+
public string PathAlias { get; set; }
1280+
}
1281+
1282+
protected class PropertyQueueEntry : EntityQueueEntry
1283+
{
1284+
public int Index { get; set; }
1285+
public IType Type { get; set; }
1286+
}
11891287
}
11901288
}

0 commit comments

Comments
 (0)