Skip to content

Commit bbe7fc9

Browse files
authored
Fix optional join optimistic lock handling (#3014)
Fixes #1235
1 parent 2f7250b commit bbe7fc9

File tree

5 files changed

+419
-10
lines changed

5 files changed

+419
-10
lines changed
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using System.Linq;
12+
using NHibernate.Cfg.MappingSchema;
13+
using NHibernate.Mapping.ByCode;
14+
using NUnit.Framework;
15+
using NUnit.Framework.Constraints;
16+
using NHibernate.Linq;
17+
18+
namespace NHibernate.Test.NHSpecificTest.GH1235
19+
{
20+
using System.Threading.Tasks;
21+
//NH-2785
22+
[TestFixture(OptimisticLockMode.None)]
23+
[TestFixture(OptimisticLockMode.Version)]
24+
[TestFixture(OptimisticLockMode.Dirty)]
25+
public class OptionalJoinFixtureAsync : TestCaseMappingByCode
26+
{
27+
private readonly OptimisticLockMode _optimisticLock;
28+
29+
public OptionalJoinFixtureAsync(OptimisticLockMode optimisticLock)
30+
{
31+
_optimisticLock = optimisticLock;
32+
}
33+
34+
protected override void OnTearDown()
35+
{
36+
using (var session = OpenSession())
37+
using (var transaction = session.BeginTransaction())
38+
{
39+
if (Dialect.SupportsTemporaryTables)
40+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
41+
else
42+
session.Delete("from System.Object");
43+
44+
transaction.Commit();
45+
}
46+
}
47+
48+
[Test]
49+
public async Task UpdateNullOptionalJoinToNotNullAsync()
50+
{
51+
object id;
52+
53+
using (var s = OpenSession())
54+
using (var t = s.BeginTransaction())
55+
{
56+
var entity = new MultiTableEntity { Name = "Bob" };
57+
id = await (s.SaveAsync(entity));
58+
await (t.CommitAsync());
59+
}
60+
61+
using (var s = OpenSession())
62+
using (var t = s.BeginTransaction())
63+
{
64+
var e = await (s.GetAsync<MultiTableEntity>(id));
65+
e.OtherName = "Sally";
66+
await (t.CommitAsync());
67+
}
68+
69+
using (var s = OpenSession())
70+
{
71+
var e = await (s.GetAsync<MultiTableEntity>(id));
72+
Assert.That(e.OtherName, Is.EqualTo("Sally"));
73+
}
74+
}
75+
76+
[Test]
77+
public async Task UpdateNullOptionalJoinToNotNullDetachedAsync()
78+
{
79+
object id;
80+
MultiTableEntity entity;
81+
82+
using (var s = OpenSession())
83+
using (var t = s.BeginTransaction())
84+
{
85+
entity = new MultiTableEntity { Name = "Bob" };
86+
id = await (s.SaveAsync(entity));
87+
await (t.CommitAsync());
88+
}
89+
90+
using (var s = OpenSession())
91+
using (var t = s.BeginTransaction())
92+
{
93+
entity.OtherName = "Sally";
94+
await (s.UpdateAsync(entity));
95+
await (t.CommitAsync());
96+
}
97+
98+
using (var s = OpenSession())
99+
{
100+
var e = await (s.GetAsync<MultiTableEntity>(id));
101+
Assert.That(e.OtherName, Is.EqualTo("Sally"));
102+
}
103+
}
104+
105+
[Test]
106+
public async Task ShouldThrowStaleStateForOptimisticLockUpdateAsync()
107+
{
108+
using (var s = OpenSession())
109+
using (var t = s.BeginTransaction())
110+
{
111+
var result = new MultiTableEntity { Name = "Bob", OtherName = "Bob" };
112+
await (s.SaveAsync(result));
113+
await (t.CommitAsync());
114+
}
115+
116+
using (var s1 = OpenSession())
117+
using (var t1 = s1.BeginTransaction())
118+
{
119+
var result = await (s1.Query<MultiTableEntity>().FirstOrDefaultAsync());
120+
121+
result.OtherName += "x";
122+
using (var s2 = OpenSession())
123+
{
124+
var result2 = await (s2.Query<MultiTableEntity>().FirstOrDefaultAsync());
125+
result2.OtherName += "y";
126+
await (t1.CommitAsync());
127+
128+
using (var t2 = s2.BeginTransaction())
129+
Assert.That(
130+
() => t2.CommitAsync(),
131+
_optimisticLock == OptimisticLockMode.None
132+
? (IResolveConstraint) Throws.Nothing
133+
: Throws.InstanceOf<StaleObjectStateException>());
134+
}
135+
}
136+
}
137+
138+
[Test]
139+
public async Task ShouldThrowStaleStateForOptimisticLockDeleteAsync()
140+
{
141+
using (var s = OpenSession())
142+
using (var t = s.BeginTransaction())
143+
{
144+
var result = new MultiTableEntity { Name = "Bob", OtherName = "Bob" };
145+
await (s.SaveAsync(result));
146+
await (t.CommitAsync());
147+
}
148+
149+
using (var s1 = OpenSession())
150+
using (var t1 = s1.BeginTransaction())
151+
{
152+
var result = await (s1.Query<MultiTableEntity>().FirstOrDefaultAsync());
153+
154+
result.OtherName += "x";
155+
using (var s2 = OpenSession())
156+
{
157+
var result2 = await (s2.Query<MultiTableEntity>().FirstOrDefaultAsync());
158+
await (s2.DeleteAsync(result2));
159+
await (t1.CommitAsync());
160+
161+
using (var t2 = s2.BeginTransaction())
162+
Assert.That(
163+
() => t2.CommitAsync(),
164+
_optimisticLock == OptimisticLockMode.None
165+
? (IResolveConstraint) Throws.Nothing
166+
: Throws.InstanceOf<StaleObjectStateException>());
167+
}
168+
}
169+
}
170+
171+
protected override HbmMapping GetMappings()
172+
{
173+
var mapper = new ModelMapper();
174+
mapper.Class<MultiTableEntity>(
175+
rc =>
176+
{
177+
rc.Id(x => x.Id, m => m.Generator(Generators.Native));
178+
rc.DynamicUpdate(true);
179+
rc.OptimisticLock(_optimisticLock);
180+
181+
if (_optimisticLock == OptimisticLockMode.Version)
182+
rc.Version(x => x.Version, _ => { });
183+
184+
rc.Property(x => x.Name);
185+
rc.Join(
186+
"SecondTable",
187+
m =>
188+
{
189+
m.Key(k => k.Column("Id"));
190+
m.Property(x => x.OtherName);
191+
m.Optional(true);
192+
});
193+
});
194+
195+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
196+
}
197+
}
198+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
3+
namespace NHibernate.Test.NHSpecificTest.GH1235
4+
{
5+
class MultiTableEntity
6+
{
7+
public virtual int Id { get; set; }
8+
public virtual int Version { get; set; }
9+
public virtual string Name { get; set; }
10+
public virtual string OtherName { get; set; }
11+
}
12+
}
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
using System.Linq;
2+
using NHibernate.Cfg.MappingSchema;
3+
using NHibernate.Mapping.ByCode;
4+
using NUnit.Framework;
5+
using NUnit.Framework.Constraints;
6+
7+
namespace NHibernate.Test.NHSpecificTest.GH1235
8+
{
9+
//NH-2785
10+
[TestFixture(OptimisticLockMode.None)]
11+
[TestFixture(OptimisticLockMode.Version)]
12+
[TestFixture(OptimisticLockMode.Dirty)]
13+
public class OptionalJoinFixture : TestCaseMappingByCode
14+
{
15+
private readonly OptimisticLockMode _optimisticLock;
16+
17+
public OptionalJoinFixture(OptimisticLockMode optimisticLock)
18+
{
19+
_optimisticLock = optimisticLock;
20+
}
21+
22+
protected override void OnTearDown()
23+
{
24+
using (var session = OpenSession())
25+
using (var transaction = session.BeginTransaction())
26+
{
27+
if (Dialect.SupportsTemporaryTables)
28+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
29+
else
30+
session.Delete("from System.Object");
31+
32+
transaction.Commit();
33+
}
34+
}
35+
36+
[Test]
37+
public void UpdateNullOptionalJoinToNotNull()
38+
{
39+
object id;
40+
41+
using (var s = OpenSession())
42+
using (var t = s.BeginTransaction())
43+
{
44+
var entity = new MultiTableEntity { Name = "Bob" };
45+
id = s.Save(entity);
46+
t.Commit();
47+
}
48+
49+
using (var s = OpenSession())
50+
using (var t = s.BeginTransaction())
51+
{
52+
var e = s.Get<MultiTableEntity>(id);
53+
e.OtherName = "Sally";
54+
t.Commit();
55+
}
56+
57+
using (var s = OpenSession())
58+
{
59+
var e = s.Get<MultiTableEntity>(id);
60+
Assert.That(e.OtherName, Is.EqualTo("Sally"));
61+
}
62+
}
63+
64+
[Test]
65+
public void UpdateNullOptionalJoinToNotNullDetached()
66+
{
67+
object id;
68+
MultiTableEntity entity;
69+
70+
using (var s = OpenSession())
71+
using (var t = s.BeginTransaction())
72+
{
73+
entity = new MultiTableEntity { Name = "Bob" };
74+
id = s.Save(entity);
75+
t.Commit();
76+
}
77+
78+
using (var s = OpenSession())
79+
using (var t = s.BeginTransaction())
80+
{
81+
entity.OtherName = "Sally";
82+
s.Update(entity);
83+
t.Commit();
84+
}
85+
86+
using (var s = OpenSession())
87+
{
88+
var e = s.Get<MultiTableEntity>(id);
89+
Assert.That(e.OtherName, Is.EqualTo("Sally"));
90+
}
91+
}
92+
93+
[Test]
94+
public void ShouldThrowStaleStateForOptimisticLockUpdate()
95+
{
96+
using (var s = OpenSession())
97+
using (var t = s.BeginTransaction())
98+
{
99+
var result = new MultiTableEntity { Name = "Bob", OtherName = "Bob" };
100+
s.Save(result);
101+
t.Commit();
102+
}
103+
104+
using (var s1 = OpenSession())
105+
using (var t1 = s1.BeginTransaction())
106+
{
107+
var result = s1.Query<MultiTableEntity>().FirstOrDefault();
108+
109+
result.OtherName += "x";
110+
using (var s2 = OpenSession())
111+
{
112+
var result2 = s2.Query<MultiTableEntity>().FirstOrDefault();
113+
result2.OtherName += "y";
114+
t1.Commit();
115+
116+
using (var t2 = s2.BeginTransaction())
117+
Assert.That(
118+
() => t2.Commit(),
119+
_optimisticLock == OptimisticLockMode.None
120+
? (IResolveConstraint) Throws.Nothing
121+
: Throws.InstanceOf<StaleObjectStateException>());
122+
}
123+
}
124+
}
125+
126+
[Test]
127+
public void ShouldThrowStaleStateForOptimisticLockDelete()
128+
{
129+
using (var s = OpenSession())
130+
using (var t = s.BeginTransaction())
131+
{
132+
var result = new MultiTableEntity { Name = "Bob", OtherName = "Bob" };
133+
s.Save(result);
134+
t.Commit();
135+
}
136+
137+
using (var s1 = OpenSession())
138+
using (var t1 = s1.BeginTransaction())
139+
{
140+
var result = s1.Query<MultiTableEntity>().FirstOrDefault();
141+
142+
result.OtherName += "x";
143+
using (var s2 = OpenSession())
144+
{
145+
var result2 = s2.Query<MultiTableEntity>().FirstOrDefault();
146+
s2.Delete(result2);
147+
t1.Commit();
148+
149+
using (var t2 = s2.BeginTransaction())
150+
Assert.That(
151+
() => t2.Commit(),
152+
_optimisticLock == OptimisticLockMode.None
153+
? (IResolveConstraint) Throws.Nothing
154+
: Throws.InstanceOf<StaleObjectStateException>());
155+
}
156+
}
157+
}
158+
159+
protected override HbmMapping GetMappings()
160+
{
161+
var mapper = new ModelMapper();
162+
mapper.Class<MultiTableEntity>(
163+
rc =>
164+
{
165+
rc.Id(x => x.Id, m => m.Generator(Generators.Native));
166+
rc.DynamicUpdate(true);
167+
rc.OptimisticLock(_optimisticLock);
168+
169+
if (_optimisticLock == OptimisticLockMode.Version)
170+
rc.Version(x => x.Version, _ => { });
171+
172+
rc.Property(x => x.Name);
173+
rc.Join(
174+
"SecondTable",
175+
m =>
176+
{
177+
m.Key(k => k.Column("Id"));
178+
m.Property(x => x.OtherName);
179+
m.Optional(true);
180+
});
181+
});
182+
183+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
184+
}
185+
}
186+
}

0 commit comments

Comments
 (0)