Skip to content

Commit 4be8405

Browse files
committed
Fix NH-2591
SVN: trunk@5500
1 parent 00c4ac7 commit 4be8405

13 files changed

+272
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace NHibernate.Test.Insertordering
2+
{
3+
public class Group
4+
{
5+
public virtual int Id { get; protected set; }
6+
public virtual string Name { get; set; }
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Data;
5+
using NHibernate.AdoNet;
6+
using NHibernate.Cfg;
7+
using NHibernate.Cfg.Loquacious;
8+
using NHibernate.Engine;
9+
using NHibernate.SqlCommand;
10+
using NHibernate.SqlTypes;
11+
using NUnit.Framework;
12+
using SharpTestsEx;
13+
14+
namespace NHibernate.Test.Insertordering
15+
{
16+
public class InsertOrderingFixture : TestCase
17+
{
18+
const int batchSize = 10;
19+
const int instancesPerEach = 12;
20+
const int typesOfEntities = 3;
21+
protected override IList Mappings
22+
{
23+
get { return new[] {"Insertordering.Mapping.hbm.xml"}; }
24+
}
25+
26+
protected override string MappingsAssembly
27+
{
28+
get { return "NHibernate.Test"; }
29+
}
30+
31+
protected override void Configure(Configuration configuration)
32+
{
33+
configuration.DataBaseIntegration(x =>
34+
{
35+
x.BatchSize = batchSize;
36+
x.OrderInserts = true;
37+
x.Batcher<StatsBatcherFactory>();
38+
});
39+
}
40+
41+
[Test]
42+
public void BatchOrdering()
43+
{
44+
using (ISession s = OpenSession())
45+
using (s.BeginTransaction())
46+
{
47+
for (int i = 0; i < instancesPerEach; i++)
48+
{
49+
var user = new User {UserName = "user-" + i};
50+
var group = new Group {Name = "group-" + i};
51+
s.Save(user);
52+
s.Save(group);
53+
user.AddMembership(group);
54+
}
55+
StatsBatcher.Reset();
56+
s.Transaction.Commit();
57+
}
58+
59+
int expectedBatchesPerEntity = (instancesPerEach / batchSize) + ((instancesPerEach % batchSize) == 0 ? 0 : 1);
60+
StatsBatcher.BatchSizes.Count.Should().Be(expectedBatchesPerEntity * typesOfEntities);
61+
62+
using (ISession s = OpenSession())
63+
{
64+
s.BeginTransaction();
65+
IList users = s.CreateQuery("from User u left join fetch u.Memberships m left join fetch m.Group").List();
66+
foreach (object user in users)
67+
{
68+
s.Delete(user);
69+
}
70+
s.Transaction.Commit();
71+
}
72+
}
73+
74+
#region Nested type: StatsBatcher
75+
76+
public class StatsBatcher : SqlClientBatchingBatcher
77+
{
78+
private static string batchSQL;
79+
private static IList<int> batchSizes = new List<int>();
80+
private static int currentBatch = -1;
81+
82+
public StatsBatcher(ConnectionManager connectionManager, IInterceptor interceptor)
83+
: base(connectionManager, interceptor) {}
84+
85+
public static IList<int> BatchSizes
86+
{
87+
get { return batchSizes; }
88+
}
89+
90+
public static void Reset()
91+
{
92+
batchSizes = new List<int>();
93+
currentBatch = -1;
94+
batchSQL = null;
95+
}
96+
97+
public override IDbCommand PrepareBatchCommand(CommandType type, SqlString sql, SqlType[] parameterTypes)
98+
{
99+
IDbCommand result = base.PrepareBatchCommand(type, sql, parameterTypes);
100+
string sqlstring = sql.ToString();
101+
if (batchSQL == null || !sqlstring.Equals(batchSQL))
102+
{
103+
currentBatch++;
104+
batchSQL = sqlstring;
105+
batchSizes.Insert(currentBatch, 0);
106+
Console.WriteLine("--------------------------------------------------------");
107+
Console.WriteLine("Preparing statement [" + batchSQL + "]");
108+
}
109+
return result;
110+
}
111+
112+
public override void AddToBatch(IExpectation expectation)
113+
{
114+
batchSizes[currentBatch]++;
115+
Console.WriteLine("Adding to batch [" + batchSQL + "]");
116+
base.AddToBatch(expectation);
117+
}
118+
119+
protected override void DoExecuteBatch(IDbCommand ps)
120+
{
121+
Console.WriteLine("executing batch [" + batchSQL + "]");
122+
Console.WriteLine("--------------------------------------------------------");
123+
batchSQL = null;
124+
base.DoExecuteBatch(ps);
125+
}
126+
}
127+
128+
#endregion
129+
130+
#region Nested type: StatsBatcherFactory
131+
132+
public class StatsBatcherFactory : IBatcherFactory
133+
{
134+
#region IBatcherFactory Members
135+
136+
public IBatcher CreateBatcher(ConnectionManager connectionManager, IInterceptor interceptor)
137+
{
138+
return new StatsBatcher(connectionManager, interceptor);
139+
}
140+
141+
#endregion
142+
}
143+
144+
#endregion
145+
}
146+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping
3+
xmlns="urn:nhibernate-mapping-2.2"
4+
namespace="NHibernate.Test.Insertordering"
5+
assembly="NHibernate.Test">
6+
7+
<class name="User" table="INS_ORD_USR">
8+
<id name="Id">
9+
<generator class="increment"/>
10+
</id>
11+
<property name="UserName" column="USR_NM" />
12+
<set name="Memberships" inverse="true" cascade="all" access="field.camelcase">
13+
<key column="USR_ID"/>
14+
<one-to-many class="Membership"/>
15+
</set>
16+
</class>
17+
18+
<class name="Group" table="INS_ORD_GRP">
19+
<id name="Id">
20+
<generator class="increment"/>
21+
</id>
22+
<property name="Name"/>
23+
</class>
24+
25+
<class name="Membership" table="INS_ORD_MEM">
26+
<id name="Id">
27+
<generator class="increment" />
28+
</id>
29+
<many-to-one name="User" class="User" column="USR_ID" cascade="all"/>
30+
<many-to-one name="Group" class="Group" column="GRP_ID" cascade="all"/>
31+
<property name="ActivationDate" type="timestamp" column="JN_DT"/>
32+
</class>
33+
</hibernate-mapping>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
3+
namespace NHibernate.Test.Insertordering
4+
{
5+
public class Membership
6+
{
7+
protected Membership() {}
8+
9+
public Membership(User user, Group @group) : this(user, group, DateTime.Now) {}
10+
11+
public Membership(User user, Group @group, DateTime activationDate)
12+
{
13+
User = user;
14+
Group = group;
15+
ActivationDate = activationDate;
16+
}
17+
18+
public virtual int Id { get; protected set; }
19+
public virtual User User { get; private set; }
20+
public virtual Group Group { get; private set; }
21+
public virtual DateTime ActivationDate { get; private set; }
22+
}
23+
}
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System.Collections.Generic;
2+
using Iesi.Collections.Generic;
3+
4+
namespace NHibernate.Test.Insertordering
5+
{
6+
public class User
7+
{
8+
private ISet<Membership> memberships;
9+
public User()
10+
{
11+
memberships = new HashedSet<Membership>();
12+
}
13+
public virtual int Id { get; protected set; }
14+
public virtual string UserName { get; set; }
15+
public virtual IEnumerable<Membership> Memberships
16+
{
17+
get { return memberships; }
18+
}
19+
20+
public virtual Membership AddMembership(Group group)
21+
{
22+
var membership = new Membership(this, group);
23+
memberships.Add(membership);
24+
return membership;
25+
}
26+
}
27+
}

src/NHibernate.Test/NHibernate.Test.csproj

+5
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,10 @@
459459
<Compile Include="Immutable\Info.cs" />
460460
<Compile Include="Immutable\Party.cs" />
461461
<Compile Include="Immutable\Plan.cs" />
462+
<Compile Include="Insertordering\Group.cs" />
463+
<Compile Include="Insertordering\InsertOrderingFixture.cs" />
464+
<Compile Include="Insertordering\Membership.cs" />
465+
<Compile Include="Insertordering\User.cs" />
462466
<Compile Include="LazyOneToOne\Employee.cs" />
463467
<Compile Include="LazyOneToOne\Employment.cs" />
464468
<Compile Include="LazyOneToOne\LazyOneToOneTest.cs" />
@@ -2472,6 +2476,7 @@
24722476
<EmbeddedResource Include="NHSpecificTest\NH1291AnonExample\Mappings.hbm.xml" />
24732477
</ItemGroup>
24742478
<ItemGroup>
2479+
<EmbeddedResource Include="Insertordering\Mapping.hbm.xml" />
24752480
<EmbeddedResource Include="NHSpecificTest\NH2530\Mappings.hbm.xml" />
24762481
<EmbeddedResource Include="DynamicProxyTests\InterfaceProxySerializationTests\ProxyImpl.hbm.xml" />
24772482
<EmbeddedResource Include="NHSpecificTest\NH2565\Mappings.hbm.xml" />

src/NHibernate/AdoNet/AbstractBatcher.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ protected void Prepare(IDbCommand cmd)
122122
}
123123
}
124124

125-
public IDbCommand PrepareBatchCommand(CommandType type, SqlString sql, SqlType[] parameterTypes)
125+
public virtual IDbCommand PrepareBatchCommand(CommandType type, SqlString sql, SqlType[] parameterTypes)
126126
{
127127
/* NH:
128128
* The code inside this block was added for a strange behaviour

src/NHibernate/Cfg/Environment.cs

+3
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ public static string Version
162162

163163
public const string LinqToHqlGeneratorsRegistry = "linqtohql.generatorsregistry";
164164

165+
/// <summary> Enable ordering of insert statements for the purpose of more effecient batching.</summary>
166+
public const string OrderInserts = "order_inserts";
167+
165168
private static readonly Dictionary<string, string> GlobalProperties;
166169

167170
private static IBytecodeProvider BytecodeProviderInstance;

src/NHibernate/Cfg/Loquacious/DbIntegrationConfiguration.cs

+17
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,18 @@ public IDbIntegrationConfiguration Each(short batchSize)
207207
return dbc;
208208
}
209209

210+
public IBatcherConfiguration OrderingInserts()
211+
{
212+
dbc.Configuration.SetProperty(Environment.OrderInserts, true.ToString().ToLowerInvariant());
213+
return this;
214+
}
215+
216+
public IBatcherConfiguration DisablingInsertsOrdering()
217+
{
218+
dbc.Configuration.SetProperty(Environment.OrderInserts, false.ToString().ToLowerInvariant());
219+
return this;
220+
}
221+
210222
#endregion
211223
}
212224

@@ -337,6 +349,11 @@ public short BatchSize
337349
set { configuration.SetProperty(Environment.BatchSize, value.ToString()); }
338350
}
339351

352+
public bool OrderInserts
353+
{
354+
set { configuration.SetProperty(Environment.OrderInserts, value.ToString().ToLowerInvariant()); }
355+
}
356+
340357
public void TransactionFactory<TFactory>() where TFactory : ITransactionFactory
341358
{
342359
configuration.SetProperty(Environment.TransactionStrategy, typeof(TFactory).AssemblyQualifiedName);

src/NHibernate/Cfg/Loquacious/IBatcherConfiguration.cs

+2
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@ public interface IBatcherConfiguration
55
{
66
IBatcherConfiguration Through<TBatcher>() where TBatcher : IBatcherFactory;
77
IDbIntegrationConfiguration Each(short batchSize);
8+
IBatcherConfiguration OrderingInserts();
9+
IBatcherConfiguration DisablingInsertsOrdering();
810
}
911
}

src/NHibernate/Cfg/Loquacious/IDbIntegrationConfiguration.cs

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public interface IDbIntegrationConfigurationProperties
4747

4848
void Batcher<TBatcher>() where TBatcher : IBatcherFactory;
4949
short BatchSize { set; }
50+
bool OrderInserts { set; }
5051

5152
void TransactionFactory<TFactory>() where TFactory : ITransactionFactory;
5253

src/NHibernate/Cfg/SettingsFactory.cs

+4
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@ public Settings BuildSettings(IDictionary<string, string> properties)
229229

230230
// TODO: Environment.BatchVersionedData
231231
settings.AdoBatchSize = PropertiesHelper.GetInt32(Environment.BatchSize, properties, 0);
232+
bool orderInserts = PropertiesHelper.GetBoolean(Environment.OrderInserts, properties, (settings.AdoBatchSize > 0));
233+
log.Info("Order SQL inserts for batching: " + EnabledDisabled(orderInserts));
234+
settings.IsOrderInsertsEnabled = orderInserts;
235+
232236
bool wrapResultSets = PropertiesHelper.GetBoolean(Environment.WrapResultSets, properties, false);
233237
log.Debug("Wrap result sets: " + EnabledDisabled(wrapResultSets));
234238
settings.IsWrapResultSetsEnabled = wrapResultSets;

src/NHibernate/nhibernate-configuration.xsd

+2-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@
114114
<xs:enumeration value="use_sql_comments" />
115115
<xs:enumeration value="format_sql" />
116116
<xs:enumeration value="collectiontype.factory_class" />
117-
</xs:restriction>
117+
<xs:enumeration value="order_inserts" />
118+
</xs:restriction>
118119
</xs:simpleType>
119120
</xs:attribute>
120121
</xs:extension>

0 commit comments

Comments
 (0)