Skip to content

Commit d27d8ab

Browse files
committed
Merge branch 'MrJul-NH-3489'
2 parents 9339720 + 8fc922d commit d27d8ab

File tree

8 files changed

+284
-7
lines changed

8 files changed

+284
-7
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.Collections.Generic;
2+
3+
namespace NHibernate.Test.NHSpecificTest.NH3489
4+
{
5+
public class Department
6+
{
7+
public Department()
8+
{
9+
Orders = new HashSet<Order>();
10+
}
11+
12+
public virtual int Id { get; set; }
13+
public virtual string Name { get; set; }
14+
public virtual ISet<Order> Orders { get; set; }
15+
}
16+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Globalization;
5+
using System.Linq;
6+
using NHibernate.Cfg;
7+
using NHibernate.Cfg.MappingSchema;
8+
using NHibernate.Mapping.ByCode;
9+
using NUnit.Framework;
10+
using Environment = NHibernate.Cfg.Environment;
11+
12+
namespace NHibernate.Test.NHSpecificTest.NH3489
13+
{
14+
[TestFixture]
15+
[Ignore("Only run to test performance.")]
16+
public class Fixture : TestCaseMappingByCode
17+
{
18+
private const int batchSize = 900;
19+
20+
protected override HbmMapping GetMappings()
21+
{
22+
var mapper = new ModelMapper();
23+
mapper.Class<Order>(cm =>
24+
{
25+
cm.Id(x => x.Id, m => m.Generator(Generators.HighLow));
26+
cm.Table("Orders");
27+
cm.Property(x => x.Name);
28+
cm.Set(x => x.Departments, m =>
29+
{
30+
m.Table("Orders_Departments");
31+
m.Key(k =>
32+
{
33+
k.Column("OrderId");
34+
k.NotNullable(true);
35+
});
36+
m.BatchSize(batchSize);
37+
m.Cascade(Mapping.ByCode.Cascade.All);
38+
}, r => r.ManyToMany(c => c.Column("DepartmentId")));
39+
});
40+
41+
mapper.Class<Department>(cm =>
42+
{
43+
cm.Id(x => x.Id, m => m.Generator(Generators.HighLow));
44+
cm.Table("Departments");
45+
cm.Set(x => x.Orders, m =>
46+
{
47+
m.Table("Orders_Departments");
48+
m.Key(k =>
49+
{
50+
k.Column("DepartmentId");
51+
k.NotNullable(true);
52+
});
53+
m.BatchSize(batchSize);
54+
m.Cascade(Mapping.ByCode.Cascade.All);
55+
m.Inverse(true);
56+
}, r => r.ManyToMany(c => c.Column("OrderId")));
57+
});
58+
59+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
60+
}
61+
62+
protected override void Configure(Configuration configuration)
63+
{
64+
base.Configure(configuration);
65+
configuration.SetProperty(Environment.BatchSize, batchSize.ToString(CultureInfo.InvariantCulture));
66+
}
67+
68+
protected override void OnSetUp()
69+
{
70+
using (ISession session = OpenSession())
71+
using (ITransaction transaction = session.BeginTransaction())
72+
{
73+
var department1 = new Department {Name = "Dep 1"};
74+
session.Save(department1);
75+
var department2 = new Department {Name = "Dep 2"};
76+
session.Save(department2);
77+
78+
for (int i = 0; i < 10000; i++)
79+
{
80+
var order = new Order {Name = "Order " + (i + 1)};
81+
order.Departments.Add(department1);
82+
order.Departments.Add(department2);
83+
session.Save(order);
84+
}
85+
86+
session.Flush();
87+
transaction.Commit();
88+
}
89+
}
90+
91+
protected override void OnTearDown()
92+
{
93+
using (ISession session = OpenSession())
94+
using (ITransaction transaction = session.BeginTransaction())
95+
{
96+
session.Delete("from System.Object");
97+
98+
session.Flush();
99+
transaction.Commit();
100+
}
101+
}
102+
103+
protected override string CacheConcurrencyStrategy
104+
{
105+
get { return null; }
106+
}
107+
108+
[Test]
109+
public void PerformanceTest()
110+
{
111+
Stopwatch stopwatch = Stopwatch.StartNew();
112+
using (ISession session = OpenSession())
113+
using (session.BeginTransaction())
114+
{
115+
IList<Order> orders = session.QueryOver<Order>().List();
116+
foreach (Order order in orders)
117+
order.Departments.ToList();
118+
}
119+
stopwatch.Stop();
120+
Console.WriteLine(stopwatch.Elapsed);
121+
}
122+
}
123+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System.Collections.Generic;
2+
3+
namespace NHibernate.Test.NHSpecificTest.NH3489
4+
{
5+
public class Order
6+
{
7+
public Order()
8+
{
9+
Departments = new HashSet<Department>();
10+
}
11+
12+
public virtual int Id { get; set; }
13+
public virtual string Name { get; set; }
14+
15+
public virtual ISet<Department> Departments { get; set; }
16+
}
17+
}

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,9 @@
745745
<Compile Include="NHSpecificTest\NH1082\SessionInterceptorThatThrowsExceptionAtBeforeTransactionCompletion.cs" />
746746
<Compile Include="NHSpecificTest\NH3571\Fixture.cs" />
747747
<Compile Include="NHSpecificTest\NH3571\Product.cs" />
748+
<Compile Include="NHSpecificTest\NH3489\Department.cs" />
749+
<Compile Include="NHSpecificTest\NH3489\Order.cs" />
750+
<Compile Include="NHSpecificTest\NH3489\Fixture.cs" />
748751
<Compile Include="NHSpecificTest\NH3459\Fixture.cs" />
749752
<Compile Include="NHSpecificTest\NH3459\Order.cs" />
750753
<Compile Include="NHSpecificTest\NH2692\Fixture.cs" />

src/NHibernate/NHibernate.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,7 @@
540540
<Compile Include="NonUniqueResultException.cs" />
541541
<Compile Include="ObjectDeletedException.cs" />
542542
<Compile Include="ObjectNotFoundException.cs" />
543+
<Compile Include="SqlCommand\BackTrackCacheParameterList.cs" />
543544
<Compile Include="Param\CriteriaNamedParameterSpecification.cs" />
544545
<Compile Include="Param\IPageableParameterSpecification.cs" />
545546
<Compile Include="Param\ParametersBackTrackExtensions.cs" />

src/NHibernate/Param/ParametersBackTrackExtensions.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,28 @@ public static class ParametersBackTrackExtensions
1111
{
1212
public static IEnumerable<int> GetEffectiveParameterLocations(this IList<Parameter> sqlParameters, string backTrackId)
1313
{
14+
var cacheParameters = sqlParameters as BackTrackCacheParameterList;
15+
return cacheParameters != null
16+
? cacheParameters.GetEffectiveParameterLocations(backTrackId)
17+
: GetEffectiveParameterLocationsSlow(sqlParameters, backTrackId);
18+
}
19+
20+
private static IEnumerable<int> GetEffectiveParameterLocationsSlow(IList<Parameter> sqlParameters, string backTrackId) {
1421
for (int i = 0; i < sqlParameters.Count; i++)
1522
{
1623
if (backTrackId.Equals(sqlParameters[i].BackTrack))
17-
{
1824
yield return i;
19-
}
2025
}
2126
}
2227

23-
public static SqlType[] GetQueryParameterTypes(this IEnumerable<IParameterSpecification> parameterSpecs, List<Parameter> sqlQueryParametersList, ISessionFactoryImplementor factory)
28+
internal static BackTrackCacheParameterList ToBackTrackCacheParameterList(this IEnumerable<Parameter> sqlParameters) {
29+
var list = new BackTrackCacheParameterList();
30+
foreach (Parameter sqlParameter in sqlParameters)
31+
list.Add(sqlParameter);
32+
return list;
33+
}
34+
35+
public static SqlType[] GetQueryParameterTypes(this IEnumerable<IParameterSpecification> parameterSpecs, IList<Parameter> sqlQueryParametersList, ISessionFactoryImplementor factory)
2436
{
2537
// NOTE: if you have a NullReferenceException probably is because the IParameterSpecification does not have the ExpectedType; use ResetEffectiveExpectedType before call this method.
2638

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
using System.Collections.Generic;
2+
using System.Collections.ObjectModel;
3+
using NHibernate.Util;
4+
5+
namespace NHibernate.SqlCommand
6+
{
7+
/// <summary>
8+
/// A list of <see cref="Parameter"/> that maintains a cache of backtrace positions for performance purpose.
9+
/// See https://nhibernate.jira.com/browse/NH-3489.
10+
/// </summary>
11+
internal class BackTrackCacheParameterList : Collection<Parameter>
12+
{
13+
private Dictionary<string, SortedSet<int>> _indexesByBackTrace;
14+
15+
private void AddIndex(Parameter parameter, int index)
16+
{
17+
var backTrack = parameter.BackTrack as string;
18+
if (backTrack == null)
19+
return;
20+
21+
SortedSet<int> indexes;
22+
if (!_indexesByBackTrace.TryGetValue(backTrack, out indexes))
23+
{
24+
indexes = new SortedSet<int>();
25+
_indexesByBackTrace.Add(backTrack, indexes);
26+
}
27+
indexes.Add(index);
28+
}
29+
30+
private void RemoveIndexes(Parameter parameter)
31+
{
32+
var backTrack = parameter.BackTrack as string;
33+
if (backTrack != null)
34+
_indexesByBackTrace.Remove(backTrack);
35+
}
36+
37+
private Dictionary<string, SortedSet<int>> BuildBackTrackCache()
38+
{
39+
var indexesByBackTrace = new Dictionary<string, SortedSet<int>>();
40+
IList<Parameter> parameters = Items;
41+
for (int i = 0; i < parameters.Count; i++)
42+
{
43+
var backTrace = parameters[i].BackTrack as string;
44+
if (backTrace != null)
45+
{
46+
SortedSet<int> locations;
47+
if (!indexesByBackTrace.TryGetValue(backTrace, out locations))
48+
{
49+
locations = new SortedSet<int>();
50+
indexesByBackTrace.Add(backTrace, locations);
51+
}
52+
locations.Add(i);
53+
}
54+
}
55+
return indexesByBackTrace;
56+
}
57+
58+
protected override void InsertItem(int index, Parameter item)
59+
{
60+
base.InsertItem(index, item);
61+
if (_indexesByBackTrace != null)
62+
AddIndex(item, index);
63+
}
64+
65+
protected override void RemoveItem(int index)
66+
{
67+
Parameter oldItem = Items[index];
68+
base.RemoveItem(index);
69+
if (_indexesByBackTrace != null)
70+
RemoveIndexes(oldItem);
71+
}
72+
73+
protected override void SetItem(int index, Parameter item)
74+
{
75+
Parameter oldItem = Items[index];
76+
base.SetItem(index, item);
77+
if (_indexesByBackTrace != null)
78+
{
79+
RemoveIndexes(oldItem);
80+
AddIndex(item, index);
81+
}
82+
}
83+
84+
protected override void ClearItems()
85+
{
86+
base.ClearItems();
87+
if (_indexesByBackTrace != null)
88+
_indexesByBackTrace.Clear();
89+
}
90+
91+
public IEnumerable<int> GetEffectiveParameterLocations(string backTrace)
92+
{
93+
if (backTrace != null)
94+
{
95+
if (_indexesByBackTrace == null)
96+
_indexesByBackTrace = BuildBackTrackCache();
97+
SortedSet<int> indexes;
98+
if (_indexesByBackTrace.TryGetValue(backTrace, out indexes))
99+
return indexes;
100+
}
101+
return ArrayHelper.EmptyIntArray;
102+
}
103+
}
104+
}

src/NHibernate/SqlCommand/SqlCommandImpl.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public class SqlCommandImpl : ISqlCommand
6060
private readonly QueryParameters queryParameters;
6161
private readonly ISessionFactoryImplementor factory;
6262
private SqlType[] parameterTypes;
63-
List<Parameter> sqlQueryParametersList;
63+
IList<Parameter> sqlQueryParametersList;
6464

6565
public SqlCommandImpl(SqlString query, ICollection<IParameterSpecification> specifications, QueryParameters queryParameters, ISessionFactoryImplementor factory)
6666
{
@@ -70,9 +70,9 @@ public SqlCommandImpl(SqlString query, ICollection<IParameterSpecification> spec
7070
this.factory = factory;
7171
}
7272

73-
public List<Parameter> SqlQueryParametersList
73+
public IList<Parameter> SqlQueryParametersList
7474
{
75-
get { return sqlQueryParametersList ?? (sqlQueryParametersList = query.GetParameters().ToList()); }
75+
get { return sqlQueryParametersList ?? (sqlQueryParametersList = query.GetParameters().ToBackTrackCacheParameterList()); }
7676
}
7777

7878
public SqlType[] ParameterTypes
@@ -103,6 +103,7 @@ public void ResetParametersIndexesForTheCommand(int singleSqlParametersOffset)
103103
{
104104
throw new AssertionFailure("singleSqlParametersOffset < 0 - this indicate a bug in NHibernate ");
105105
}
106+
106107
// due to IType.NullSafeSet(System.Data.IDbCommand , object, int, ISessionImplementor) the SqlType[] is supposed to be in a certain sequence.
107108
// this mean that found the first location of a parameter for the IType span, the others are in secuence
108109
foreach (IParameterSpecification specification in Specifications)
@@ -111,7 +112,7 @@ public void ResetParametersIndexesForTheCommand(int singleSqlParametersOffset)
111112
int[] effectiveParameterLocations = SqlQueryParametersList.GetEffectiveParameterLocations(firstParameterId).ToArray();
112113
if (effectiveParameterLocations.Length > 0) // Parameters previously present might have been removed from the SQL at a later point.
113114
{
114-
int firstParamNameIndex = effectiveParameterLocations.First() + singleSqlParametersOffset;
115+
int firstParamNameIndex = effectiveParameterLocations[0] + singleSqlParametersOffset;
115116
foreach (int location in effectiveParameterLocations)
116117
{
117118
int parameterSpan = specification.ExpectedType.GetColumnSpan(factory);

0 commit comments

Comments
 (0)