Skip to content

Commit 4f8c972

Browse files
committed
Refactor sequential select
1 parent 14c5cdb commit 4f8c972

File tree

7 files changed

+202
-102
lines changed

7 files changed

+202
-102
lines changed

src/NHibernate/Async/Loader/Loader.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -793,7 +793,7 @@ private async Task LoadFromResultSetAsync(DbDataReader rs, int i, object obj, st
793793
? EntityAliases[i].SuffixedPropertyAliases
794794
: GetSubclassEntityAliases(i, persister);
795795

796-
object[] values = await (persister.HydrateAsync(rs, id, obj, rootPersister, cols, eagerPropertyFetch, session, cancellationToken)).ConfigureAwait(false);
796+
object[] values = await (persister.HydrateAsync(rs, id, obj, cols, eagerPropertyFetch, session, cancellationToken)).ConfigureAwait(false);
797797

798798
object rowId = persister.HasRowId ? rs[EntityAliases[i].RowIdAlias] : null;
799799

src/NHibernate/Async/Persister/Entity/AbstractEntityPersister.cs

+21-15
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
using Property=NHibernate.Mapping.Property;
3737
using NHibernate.SqlTypes;
3838
using System.Linq;
39+
using System.Linq.Expressions;
3940
using NHibernate.Bytecode;
4041

4142
namespace NHibernate.Persister.Entity
@@ -339,7 +340,21 @@ protected async Task<int> DehydrateAsync(object id, object[] fields, object rowI
339340
/// Unmarshall the fields of a persistent instance from a result set,
340341
/// without resolving associations or collections
341342
/// </summary>
342-
public async Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj, ILoadable rootLoadable,
343+
public Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj, ILoadable rootLoadable,
344+
string[][] suffixedPropertyColumns, bool allProperties, ISessionImplementor session, CancellationToken cancellationToken)
345+
{
346+
if (cancellationToken.IsCancellationRequested)
347+
{
348+
return Task.FromCanceled<object[]>(cancellationToken);
349+
}
350+
return HydrateAsync(rs, id, obj, suffixedPropertyColumns, allProperties, session, cancellationToken);
351+
}
352+
353+
/// <summary>
354+
/// Unmarshall the fields of a persistent instance from a result set,
355+
/// without resolving associations or collections
356+
/// </summary>
357+
public async Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj,
343358
string[][] suffixedPropertyColumns, bool allProperties, ISessionImplementor session, CancellationToken cancellationToken)
344359
{
345360
cancellationToken.ThrowIfCancellationRequested();
@@ -348,9 +363,7 @@ public async Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj,
348363
log.Debug("Hydrating entity: {0}", MessageHelper.InfoString(this, id, Factory));
349364
}
350365

351-
AbstractEntityPersister rootPersister = (AbstractEntityPersister)rootLoadable;
352-
353-
bool hasDeferred = rootPersister.HasSequentialSelect;
366+
bool hasDeferred = HasSequentialSelect;
354367
DbCommand sequentialSelect = null;
355368
DbDataReader sequentialResultSet = null;
356369
bool sequentialSelectEmpty = false;
@@ -359,12 +372,12 @@ public async Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj,
359372
{
360373
if (hasDeferred)
361374
{
362-
SqlString sql = rootPersister.GetSequentialSelect(EntityName);
375+
var sql = GetSequentialSelect();
363376
if (sql != null)
364377
{
365378
//TODO: I am not so sure about the exception handling in this bit!
366379
sequentialSelect = await (session.Batcher.PrepareCommandAsync(CommandType.Text, sql, IdentifierType.SqlTypes(factory), cancellationToken)).ConfigureAwait(false);
367-
await (rootPersister.IdentifierType.NullSafeSetAsync(sequentialSelect, id, 0, session, cancellationToken)).ConfigureAwait(false);
380+
await (IdentifierType.NullSafeSetAsync(sequentialSelect, id, 0, session, cancellationToken)).ConfigureAwait(false);
368381
sequentialResultSet = await (session.Batcher.ExecuteReaderAsync(sequentialSelect, cancellationToken)).ConfigureAwait(false);
369382
if (!await (sequentialResultSet.ReadAsync(cancellationToken)).ConfigureAwait(false))
370383
{
@@ -394,11 +407,9 @@ public async Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj,
394407
}
395408
}
396409

397-
string[] propNames = PropertyNames;
398410
IType[] types = PropertyTypes;
399411
object[] values = new object[types.Length];
400412
bool[] laziness = PropertyLaziness;
401-
string[] propSubclassNames = SubclassPropertySubclassNameClosure;
402413

403414
for (int i = 0; i < types.Length; i++)
404415
{
@@ -409,8 +420,7 @@ public async Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj,
409420
else if (allProperties || !laziness[i])
410421
{
411422
//decide which ResultSet to get the property value from:
412-
bool propertyIsDeferred = hasDeferred
413-
&& rootPersister.IsSubclassPropertyDeferred(propNames[i], propSubclassNames[i]);
423+
var propertyIsDeferred = hasDeferred && IsPropertyDeferred(i);
414424
if (propertyIsDeferred && sequentialSelectEmpty)
415425
{
416426
values[i] = null;
@@ -428,15 +438,11 @@ public async Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj,
428438
}
429439
}
430440

431-
if (sequentialResultSet != null)
432-
{
433-
sequentialResultSet.Close();
434-
}
435-
436441
return values;
437442
}
438443
finally
439444
{
445+
sequentialResultSet?.Close();
440446
if (sequentialSelect != null)
441447
{
442448
session.Batcher.CloseCommand(sequentialSelect, sequentialResultSet);

src/NHibernate/Async/Persister/Entity/ILoadable.cs

+40
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
//------------------------------------------------------------------------------
99

1010

11+
using System;
1112
using NHibernate.Type;
1213
using NHibernate.Engine;
1314
using System.Data.Common;
@@ -23,7 +24,46 @@ public partial interface ILoadable : IEntityPersister
2324
/// <summary>
2425
/// Retrieve property values from one row of a result set
2526
/// </summary>
27+
//Since 5.3
28+
[Obsolete("Use the extension method without the rootLoadable parameter instead")]
2629
Task<object[]> HydrateAsync(DbDataReader rs, object id, object obj, ILoadable rootLoadable, string[][] suffixedPropertyColumns,
2730
bool allProperties, ISessionImplementor session, CancellationToken cancellationToken);
2831
}
32+
33+
public static partial class LoadableExtensions
34+
{
35+
public static Task<object[]> HydrateAsync(
36+
this ILoadable loadable,
37+
DbDataReader rs,
38+
object id,
39+
object obj,
40+
string[][] suffixedPropertyColumns,
41+
bool allProperties,
42+
ISessionImplementor session, CancellationToken cancellationToken)
43+
{
44+
if (cancellationToken.IsCancellationRequested)
45+
{
46+
return Task.FromCanceled<object[]>(cancellationToken);
47+
}
48+
try
49+
{
50+
if (loadable is AbstractEntityPersister entityPersister)
51+
{
52+
return entityPersister.HydrateAsync(rs, id, obj, suffixedPropertyColumns, allProperties, session, cancellationToken);
53+
}
54+
55+
var rootLoadable = loadable.RootEntityName == loadable.EntityName
56+
? loadable
57+
: (ILoadable) loadable.Factory.GetEntityPersister(loadable.RootEntityName);
58+
59+
#pragma warning disable 618
60+
return loadable.HydrateAsync(rs, id, obj, rootLoadable, suffixedPropertyColumns, allProperties, session, cancellationToken);
61+
#pragma warning restore 618
62+
}
63+
catch (Exception ex)
64+
{
65+
return Task.FromException<object[]>(ex);
66+
}
67+
}
68+
}
2969
}

src/NHibernate/Loader/Loader.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1144,7 +1144,7 @@ private void LoadFromResultSet(DbDataReader rs, int i, object obj, string instan
11441144
? EntityAliases[i].SuffixedPropertyAliases
11451145
: GetSubclassEntityAliases(i, persister);
11461146

1147-
object[] values = persister.Hydrate(rs, id, obj, rootPersister, cols, eagerPropertyFetch, session);
1147+
object[] values = persister.Hydrate(rs, id, obj, cols, eagerPropertyFetch, session);
11481148

11491149
object rowId = persister.HasRowId ? rs[EntityAliases[i].RowIdAlias] : null;
11501150

src/NHibernate/Persister/Entity/AbstractEntityPersister.cs

+87-14
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
using Property=NHibernate.Mapping.Property;
2727
using NHibernate.SqlTypes;
2828
using System.Linq;
29+
using System.Linq.Expressions;
2930
using NHibernate.Bytecode;
3031

3132
namespace NHibernate.Persister.Entity
@@ -258,6 +259,7 @@ public virtual void BindValues(DbCommand ps)
258259

259260
// This must be a Lazy<T>, because instances of this class must be thread safe.
260261
private readonly Lazy<string[]> defaultUniqueKeyPropertyNamesForSelectId;
262+
private readonly Dictionary<string, int> propertyTableNumbersByNameAndSubclass = new Dictionary<string, int>();
261263

262264
protected AbstractEntityPersister(PersistentClass persistentClass, ICacheConcurrencyStrategy cache,
263265
ISessionFactoryImplementor factory)
@@ -443,6 +445,8 @@ protected AbstractEntityPersister(PersistentClass persistentClass, ICacheConcurr
443445
definedBySubclass.Add(isDefinedBySubclass);
444446
propNullables.Add(prop.IsOptional || isDefinedBySubclass); //TODO: is this completely correct?
445447
types.Add(prop.Type);
448+
propertyTableNumbersByNameAndSubclass[prop.PersistentClass.EntityName + '.' + prop.Name] =
449+
persistentClass.GetJoinNumber(prop);
446450

447451
string[] cols = new string[prop.ColumnSpan];
448452
string[] forms = new string[prop.ColumnSpan];
@@ -1109,6 +1113,16 @@ protected virtual bool IsIdOfTable(int property, int table)
11091113

11101114
protected abstract int GetSubclassPropertyTableNumber(int i);
11111115

1116+
internal int GetSubclassPropertyTableNumber(string propertyName, string entityName)
1117+
{
1118+
IType type = propertyMapping.ToType(propertyName);
1119+
if (type.IsAssociationType && ((IAssociationType) type).UseLHSPrimaryKey)
1120+
return 0;
1121+
int tabnum;
1122+
propertyTableNumbersByNameAndSubclass.TryGetValue(entityName + '.' + propertyName, out tabnum);
1123+
return tabnum;
1124+
}
1125+
11121126
public abstract string FilterFragment(string alias);
11131127

11141128
protected internal virtual string DiscriminatorAlias
@@ -1161,11 +1175,18 @@ protected bool IsDeleteCallable(int j)
11611175
return deleteCallable[j];
11621176
}
11631177

1178+
//Since 5.3
1179+
[Obsolete("This method has no more usage in NHibernate and will be removed in a future version.")]
11641180
protected virtual bool IsSubclassPropertyDeferred(string propertyName, string entityName)
11651181
{
11661182
return false;
11671183
}
11681184

1185+
protected virtual bool IsPropertyDeferred(int propertyIndex)
1186+
{
1187+
return false;
1188+
}
1189+
11691190
protected virtual bool IsSubclassTableSequentialSelect(int table)
11701191
{
11711192
return false;
@@ -2590,15 +2611,23 @@ protected int Dehydrate(object id, object[] fields, object rowId, bool[] include
25902611
/// </summary>
25912612
public object[] Hydrate(DbDataReader rs, object id, object obj, ILoadable rootLoadable,
25922613
string[][] suffixedPropertyColumns, bool allProperties, ISessionImplementor session)
2614+
{
2615+
return Hydrate(rs, id, obj, suffixedPropertyColumns, allProperties, session);
2616+
}
2617+
2618+
/// <summary>
2619+
/// Unmarshall the fields of a persistent instance from a result set,
2620+
/// without resolving associations or collections
2621+
/// </summary>
2622+
public object[] Hydrate(DbDataReader rs, object id, object obj,
2623+
string[][] suffixedPropertyColumns, bool allProperties, ISessionImplementor session)
25932624
{
25942625
if (log.IsDebugEnabled())
25952626
{
25962627
log.Debug("Hydrating entity: {0}", MessageHelper.InfoString(this, id, Factory));
25972628
}
25982629

2599-
AbstractEntityPersister rootPersister = (AbstractEntityPersister)rootLoadable;
2600-
2601-
bool hasDeferred = rootPersister.HasSequentialSelect;
2630+
bool hasDeferred = HasSequentialSelect;
26022631
DbCommand sequentialSelect = null;
26032632
DbDataReader sequentialResultSet = null;
26042633
bool sequentialSelectEmpty = false;
@@ -2607,12 +2636,12 @@ public object[] Hydrate(DbDataReader rs, object id, object obj, ILoadable rootLo
26072636
{
26082637
if (hasDeferred)
26092638
{
2610-
SqlString sql = rootPersister.GetSequentialSelect(EntityName);
2639+
var sql = GetSequentialSelect();
26112640
if (sql != null)
26122641
{
26132642
//TODO: I am not so sure about the exception handling in this bit!
26142643
sequentialSelect = session.Batcher.PrepareCommand(CommandType.Text, sql, IdentifierType.SqlTypes(factory));
2615-
rootPersister.IdentifierType.NullSafeSet(sequentialSelect, id, 0, session);
2644+
IdentifierType.NullSafeSet(sequentialSelect, id, 0, session);
26162645
sequentialResultSet = session.Batcher.ExecuteReader(sequentialSelect);
26172646
if (!sequentialResultSet.Read())
26182647
{
@@ -2642,11 +2671,9 @@ public object[] Hydrate(DbDataReader rs, object id, object obj, ILoadable rootLo
26422671
}
26432672
}
26442673

2645-
string[] propNames = PropertyNames;
26462674
IType[] types = PropertyTypes;
26472675
object[] values = new object[types.Length];
26482676
bool[] laziness = PropertyLaziness;
2649-
string[] propSubclassNames = SubclassPropertySubclassNameClosure;
26502677

26512678
for (int i = 0; i < types.Length; i++)
26522679
{
@@ -2657,8 +2684,7 @@ public object[] Hydrate(DbDataReader rs, object id, object obj, ILoadable rootLo
26572684
else if (allProperties || !laziness[i])
26582685
{
26592686
//decide which ResultSet to get the property value from:
2660-
bool propertyIsDeferred = hasDeferred
2661-
&& rootPersister.IsSubclassPropertyDeferred(propNames[i], propSubclassNames[i]);
2687+
var propertyIsDeferred = hasDeferred && IsPropertyDeferred(i);
26622688
if (propertyIsDeferred && sequentialSelectEmpty)
26632689
{
26642690
values[i] = null;
@@ -2676,15 +2702,11 @@ public object[] Hydrate(DbDataReader rs, object id, object obj, ILoadable rootLo
26762702
}
26772703
}
26782704

2679-
if (sequentialResultSet != null)
2680-
{
2681-
sequentialResultSet.Close();
2682-
}
2683-
26842705
return values;
26852706
}
26862707
finally
26872708
{
2709+
sequentialResultSet?.Close();
26882710
if (sequentialSelect != null)
26892711
{
26902712
session.Batcher.CloseCommand(sequentialSelect, sequentialResultSet);
@@ -2702,11 +2724,18 @@ protected bool UseGetGeneratedKeys()
27022724
return Factory.Settings.IsGetGeneratedKeysEnabled;
27032725
}
27042726

2727+
//Since 5.3
2728+
[Obsolete("This method has no more usage in NHibernate and will be removed in a future version.")]
27052729
protected virtual SqlString GetSequentialSelect(string entityName)
27062730
{
27072731
throw new NotSupportedException("no sequential selects");
27082732
}
27092733

2734+
protected virtual SqlString GetSequentialSelect()
2735+
{
2736+
throw new NotSupportedException("no sequential select");
2737+
}
2738+
27102739
/// <summary>
27112740
/// Perform an SQL INSERT, and then retrieve a generated identifier.
27122741
/// </summary>
@@ -4491,5 +4520,49 @@ public string GetInfoString()
44914520
return MessageHelper.InfoString(this);
44924521
}
44934522
#endregion
4523+
4524+
internal SqlString GenerateSequentialSelect(ILoadable persister)
4525+
{
4526+
//figure out which tables need to be fetched (only those that contains at least a no-lazy-property)
4527+
AbstractEntityPersister subclassPersister = (AbstractEntityPersister) persister;
4528+
var tableNumbers = new HashSet<int>();
4529+
string[] props = subclassPersister.PropertyNames;
4530+
string[] classes = subclassPersister.PropertySubclassNames;
4531+
for (int i = 0; i < props.Length; i++)
4532+
{
4533+
int propTableNumber = GetSubclassPropertyTableNumber(props[i], classes[i]);
4534+
if (IsSubclassTableSequentialSelect(propTableNumber) && !IsSubclassTableLazy(propTableNumber))
4535+
{
4536+
tableNumbers.Add(propTableNumber);
4537+
}
4538+
}
4539+
if ((tableNumbers.Count == 0))
4540+
return null;
4541+
4542+
//figure out which columns are needed (excludes lazy-properties)
4543+
List<int> columnNumbers = new List<int>();
4544+
int[] columnTableNumbers = SubclassColumnTableNumberClosure;
4545+
for (int i = 0; i < SubclassColumnClosure.Length; i++)
4546+
{
4547+
if (tableNumbers.Contains(columnTableNumbers[i]))
4548+
{
4549+
columnNumbers.Add(i);
4550+
}
4551+
}
4552+
4553+
//figure out which formulas are needed (excludes lazy-properties)
4554+
List<int> formulaNumbers = new List<int>();
4555+
int[] formulaTableNumbers = SubclassFormulaTableNumberClosure;
4556+
for (int i = 0; i < SubclassFormulaTemplateClosure.Length; i++)
4557+
{
4558+
if (tableNumbers.Contains(formulaTableNumbers[i]))
4559+
{
4560+
formulaNumbers.Add(i);
4561+
}
4562+
}
4563+
4564+
//render the SQL
4565+
return RenderSelect(tableNumbers.ToArray(), columnNumbers.ToArray(), formulaNumbers.ToArray());
4566+
}
44944567
}
44954568
}

0 commit comments

Comments
 (0)