Skip to content

Commit 12278f1

Browse files
RogerKratzhazzik
authored andcommitted
NH-3932 - Prevent Merge() to fire unnecessary collection updates
1 parent 0041b52 commit 12278f1

20 files changed

+838
-31
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using NHibernate.Test.NHSpecificTest.NH3932.Model;
2+
3+
namespace NHibernate.Test.NHSpecificTest.NH3932
4+
{
5+
public class BagFixture : Fixture
6+
{
7+
protected override bool CareAboutOrder => false;
8+
9+
protected override IParent CreateParent(int numberOfChildren)
10+
{
11+
var parent = new BagParent();
12+
for (var i = 0; i < numberOfChildren; i++)
13+
{
14+
parent.Children.Add(new Child { Name = "child" + i });
15+
}
16+
return parent;
17+
}
18+
}
19+
}
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
using System.Collections;
2+
using NHibernate.Test.NHSpecificTest.NH3932.Model;
3+
using NUnit.Framework;
4+
5+
namespace NHibernate.Test.NHSpecificTest.NH3932
6+
{
7+
public abstract class Fixture : BugTestCase
8+
{
9+
private IParent storedParent;
10+
protected abstract bool CareAboutOrder { get; }
11+
12+
[Test]
13+
public void ShouldKeepDirtyCollectionDirtyAfterMergingClone()
14+
{
15+
using (var s = OpenSession())
16+
{
17+
using (s.BeginTransaction())
18+
{
19+
s.Lock(storedParent, LockMode.None);
20+
storedParent.ClearChildren();
21+
Assert.IsTrue(s.IsDirty());
22+
23+
var loadedClone = storedParent.Clone();
24+
s.Merge(loadedClone);
25+
Assert.IsTrue(s.IsDirty());
26+
}
27+
}
28+
}
29+
30+
[Test]
31+
public void ShouldCareAboutOrder_MergeObjectNotInSession()
32+
{
33+
var parent = CreateParent(2);
34+
using (var s = OpenSession())
35+
{
36+
using (var tx = s.BeginTransaction())
37+
{
38+
s.Save(parent);
39+
tx.Commit();
40+
}
41+
}
42+
var parentClone = parent.Clone();
43+
parentClone.ReverseChildren();
44+
using (var s = OpenSession())
45+
{
46+
s.SessionFactory.Statistics.Clear();
47+
using (var tx = s.BeginTransaction())
48+
{
49+
s.Merge(parentClone);
50+
tx.Commit();
51+
}
52+
Assert.That(s.SessionFactory.Statistics.EntityUpdateCount, CareAboutOrder ? Is.EqualTo(1) : Is.EqualTo(0));
53+
}
54+
}
55+
56+
[Test]
57+
public void ShouldCareAboutOrder_MergeObjectInSession()
58+
{
59+
var parent = CreateParent(2);
60+
using (var s = OpenSession())
61+
{
62+
using (var tx = s.BeginTransaction())
63+
{
64+
s.Save(parent);
65+
tx.Commit();
66+
}
67+
}
68+
var parentClone = parent.Clone();
69+
parentClone.ReverseChildren();
70+
using (var s = OpenSession())
71+
{
72+
s.SessionFactory.Statistics.Clear();
73+
using (var tx = s.BeginTransaction())
74+
{
75+
s.Lock(parent, LockMode.None);
76+
s.Merge(parentClone);
77+
tx.Commit();
78+
}
79+
Assert.That(s.SessionFactory.Statistics.EntityUpdateCount, CareAboutOrder ? Is.EqualTo(1) : Is.EqualTo(0));
80+
}
81+
}
82+
83+
[Test]
84+
public void MergeCleanCloneShouldNotResultInUpdate()
85+
{
86+
var parentClone = storedParent.Clone();
87+
using (var s = OpenSession())
88+
{
89+
s.SessionFactory.Statistics.Clear();
90+
using (var tx = s.BeginTransaction())
91+
{
92+
s.Merge(parentClone);
93+
tx.Commit();
94+
}
95+
Assert.That(s.SessionFactory.Statistics.EntityUpdateCount, Is.EqualTo(0));
96+
}
97+
}
98+
99+
[Test]
100+
public void MergeCleanCloneShouldNotMakeSessionDirty()
101+
{
102+
var parentClone = storedParent.Clone();
103+
using (var s = OpenSession())
104+
{
105+
s.SessionFactory.Statistics.Clear();
106+
using (var tx = s.BeginTransaction())
107+
{
108+
s.Merge(parentClone);
109+
Assert.That(s.IsDirty(), Is.False);
110+
tx.Commit();
111+
}
112+
}
113+
}
114+
115+
[Test]
116+
public void MergeCloneOnNonCloneShouldNotResultInUpdate()
117+
{
118+
var parentClone = storedParent.Clone();
119+
using (var s = OpenSession())
120+
{
121+
s.SessionFactory.Statistics.Clear();
122+
using (var tx = s.BeginTransaction())
123+
{
124+
s.Lock(storedParent, LockMode.None);
125+
s.Merge(parentClone);
126+
tx.Commit();
127+
}
128+
Assert.That(s.SessionFactory.Statistics.EntityUpdateCount, Is.EqualTo(0));
129+
}
130+
}
131+
132+
[Test]
133+
public void MergeCloneOnNonCloneShouldNotMakeSessionDirty()
134+
{
135+
var parentClone = storedParent.Clone();
136+
using (var s = OpenSession())
137+
{
138+
s.SessionFactory.Statistics.Clear();
139+
using (var tx = s.BeginTransaction())
140+
{
141+
s.Lock(storedParent, LockMode.None);
142+
s.Merge(parentClone);
143+
Assert.That(s.IsDirty(), Is.False);
144+
tx.Commit();
145+
}
146+
}
147+
}
148+
149+
[Test]
150+
public void MoreElementsInTargetShouldBeTreatedAsDirty()
151+
{
152+
var parent = CreateParent(2);
153+
using (var s = OpenSession())
154+
{
155+
using (var tx = s.BeginTransaction())
156+
{
157+
s.Save(parent);
158+
tx.Commit();
159+
}
160+
}
161+
var parentClone = parent.Clone();
162+
parentClone.RemoveLastChild();
163+
using (var s = OpenSession())
164+
{
165+
s.SessionFactory.Statistics.Clear();
166+
using (var tx = s.BeginTransaction())
167+
{
168+
s.Merge(parentClone);
169+
tx.Commit();
170+
}
171+
Assert.That(s.SessionFactory.Statistics.EntityUpdateCount, Is.EqualTo(1));
172+
}
173+
}
174+
175+
protected abstract IParent CreateParent(int numberOfChildren);
176+
177+
protected override void OnSetUp()
178+
{
179+
storedParent = CreateParent(1);
180+
using (var s = OpenSession())
181+
{
182+
using (var tx = s.BeginTransaction())
183+
{
184+
s.Save(storedParent);
185+
tx.Commit();
186+
}
187+
}
188+
}
189+
190+
protected override void OnTearDown()
191+
{
192+
using (var s = sessions.OpenSession())
193+
{
194+
using (var tx = s.BeginTransaction())
195+
{
196+
s.Delete("from " + storedParent.GetType());
197+
tx.Commit();
198+
}
199+
}
200+
}
201+
202+
protected override void Configure(Cfg.Configuration configuration)
203+
{
204+
configuration.SetProperty(Cfg.Environment.GenerateStatistics, "true");
205+
}
206+
207+
protected override IList Mappings => new[]{"NHSpecificTest." + BugNumber + ".Model.Mappings.hbm.xml"};
208+
}
209+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using NHibernate.Test.NHSpecificTest.NH3932.Model;
2+
3+
namespace NHibernate.Test.NHSpecificTest.NH3932
4+
{
5+
public class IdBagFixture : Fixture
6+
{
7+
protected override bool CareAboutOrder => false;
8+
9+
protected override IParent CreateParent(int numberOfChildren)
10+
{
11+
var parent = new IdBagParent();
12+
for (var i = 0; i < numberOfChildren; i++)
13+
{
14+
parent.Children.Add(new Child { Name = "child" + i });
15+
}
16+
return parent;
17+
}
18+
}
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using NHibernate.Test.NHSpecificTest.NH3932.Model;
2+
3+
namespace NHibernate.Test.NHSpecificTest.NH3932
4+
{
5+
public class ListFixture : Fixture
6+
{
7+
protected override bool CareAboutOrder => true;
8+
9+
protected override IParent CreateParent(int numberOfChildren)
10+
{
11+
var parent = new ListParent();
12+
for (var i = 0; i < numberOfChildren; i++)
13+
{
14+
parent.Children.Add(new Child { Name = "child" + i });
15+
}
16+
return parent;
17+
}
18+
}
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using NHibernate.Test.NHSpecificTest.NH3932.Model;
2+
3+
namespace NHibernate.Test.NHSpecificTest.NH3932
4+
{
5+
public class MapFixture : Fixture
6+
{
7+
protected override bool CareAboutOrder => false;
8+
9+
protected override IParent CreateParent(int numberOfChildren)
10+
{
11+
var parent = new MapParent();
12+
for (var i = 0; i < numberOfChildren; i++)
13+
{
14+
parent.Children.Add(i, new Child{Name="child" + i});
15+
}
16+
return parent;
17+
}
18+
}
19+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
4+
namespace NHibernate.Test.NHSpecificTest.NH3932.Model
5+
{
6+
public class BagParent : IParent
7+
{
8+
public BagParent()
9+
{
10+
Children = new List<Child>();
11+
}
12+
13+
public virtual int Id { get; set; }
14+
public virtual int Version { get; set; }
15+
public virtual IList<Child> Children { get; set; }
16+
17+
public virtual IParent Clone()
18+
{
19+
return new BagParent
20+
{
21+
Id = Id,
22+
Version = Version,
23+
Children = new List<Child>(Children)
24+
};
25+
}
26+
27+
public virtual void ReverseChildren()
28+
{
29+
Children = new List<Child>(Children.Reverse());
30+
}
31+
32+
public virtual void ClearChildren()
33+
{
34+
Children.Clear();
35+
}
36+
37+
public virtual void RemoveLastChild()
38+
{
39+
Children.Remove(Children.Last());
40+
}
41+
42+
public override bool Equals(object obj)
43+
{
44+
var that = obj as BagParent;
45+
return that?.Id == Id;
46+
}
47+
48+
public override int GetHashCode()
49+
{
50+
return 0; //for simplicity - care only about equals impl
51+
}
52+
}
53+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace NHibernate.Test.NHSpecificTest.NH3932.Model
2+
{
3+
public class Child
4+
{
5+
public virtual int Id { get; set; }
6+
public virtual string Name { get; set; }
7+
8+
public override bool Equals(object obj)
9+
{
10+
if (Id == 0)
11+
return base.Equals(obj);
12+
var that = obj as Child;
13+
return that?.Id == Id;
14+
}
15+
16+
public override int GetHashCode()
17+
{
18+
return 0; //for simplicity - care only about equals impl
19+
}
20+
}
21+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace NHibernate.Test.NHSpecificTest.NH3932.Model
2+
{
3+
public interface IParent
4+
{
5+
IParent Clone();
6+
void ReverseChildren();
7+
void ClearChildren();
8+
void RemoveLastChild();
9+
}
10+
}

0 commit comments

Comments
 (0)