Skip to content

Commit ecf8d78

Browse files
authored
Enable one-to-one optimistic lock handling in mapping (nhibernate#3216)
Related to nhibernate#3204
1 parent c8e3940 commit ecf8d78

File tree

8 files changed

+255
-3
lines changed

8 files changed

+255
-3
lines changed

doc/reference/modules/basic_mapping.xml

+11-1
Original file line numberDiff line numberDiff line change
@@ -2385,6 +2385,7 @@
23852385
<area id="onetoone9" coords="10 60"/>
23862386
<area id="onetoone10" coords="11 60"/>
23872387
<area id="onetoone11" coords="12 60"/>
2388+
<area id="onetoone12" coords="13 60"/>
23882389
</areaspec>
23892390
<programlisting><![CDATA[<one-to-one
23902391
name="propertyName"
@@ -2398,6 +2399,7 @@
23982399
formula="any SQL expression"
23992400
entity-name="entityName"
24002401
foreign-key="foreignKeyName"
2402+
optimistic-lock="true|false"
24012403
/>]]></programlisting>
24022404
<calloutlist>
24032405
<callout arearefs="onetoone1">
@@ -2471,12 +2473,20 @@
24712473
<literal>entity-name</literal> (optional): The entity name of the associated class.
24722474
</para>
24732475
</callout>
2474-
<callout arearefs="manytoone11">
2476+
<callout arearefs="onetoone11">
24752477
<para>
24762478
<literal>foreign-key</literal> (optional): Specifies the name of the foreign key
24772479
constraint for DDL generation.
24782480
</para>
24792481
</callout>
2482+
<callout arearefs="onetoone12">
2483+
<para>
2484+
<literal>optimistic-lock</literal> (optional - defaults to <literal>true</literal>):
2485+
Specifies that updates to this property do or do not require acquisition of the
2486+
optimistic lock. In other words, determines if a version increment should occur when
2487+
this property is dirty.
2488+
</para>
2489+
</callout>
24802490
</calloutlist>
24812491
</programlistingco>
24822492

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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;
12+
using NHibernate.Cfg.MappingSchema;
13+
using NHibernate.Mapping.ByCode;
14+
using NUnit.Framework;
15+
16+
namespace NHibernate.Test.NHSpecificTest.GH3204
17+
{
18+
using System.Threading.Tasks;
19+
[TestFixture(true)]
20+
[TestFixture(false)]
21+
[TestFixture(null)]
22+
public class OneToOneOptimisticLockFixtureAsync : TestCaseMappingByCode
23+
{
24+
private readonly bool? _optimisticLock;
25+
private Guid _id;
26+
27+
public OneToOneOptimisticLockFixtureAsync(bool? optimisticLock)
28+
{
29+
_optimisticLock = optimisticLock;
30+
}
31+
32+
protected override HbmMapping GetMappings()
33+
{
34+
var mapper = new ModelMapper();
35+
mapper.Class<Entity>(
36+
rc =>
37+
{
38+
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
39+
rc.Property(x => x.Name);
40+
rc.Version(x => x.Version, m => { });
41+
rc.OneToOne(
42+
x => x.OneToOne,
43+
m =>
44+
{
45+
m.PropertyReference(x => x.Parent);
46+
m.ForeignKey("none");
47+
m.Cascade(Mapping.ByCode.Cascade.All);
48+
if (_optimisticLock.HasValue)
49+
m.OptimisticLock(_optimisticLock.Value);
50+
});
51+
});
52+
53+
mapper.Class<Child>(
54+
rc =>
55+
{
56+
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
57+
rc.Property(x => x.Name);
58+
rc.ManyToOne(
59+
x => x.Parent,
60+
m =>
61+
{
62+
m.Unique(true);
63+
m.ForeignKey("none");
64+
});
65+
});
66+
67+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
68+
}
69+
70+
protected override void OnSetUp()
71+
{
72+
using (var session = OpenSession())
73+
using (var transaction = session.BeginTransaction())
74+
{
75+
var e1 = new Entity { Name = "Bob" };
76+
session.Save(e1);
77+
transaction.Commit();
78+
_id = e1.Id;
79+
}
80+
}
81+
82+
protected override void OnTearDown()
83+
{
84+
using (var session = OpenSession())
85+
using (var transaction = session.BeginTransaction())
86+
{
87+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
88+
89+
transaction.Commit();
90+
}
91+
}
92+
93+
[Test]
94+
public async Task GetAsync()
95+
{
96+
using (var session = OpenSession())
97+
using (var transaction = session.BeginTransaction())
98+
{
99+
var result = await (session.GetAsync<Entity>(_id));
100+
101+
var child = new Child() { Parent = result };
102+
result.OneToOne = child;
103+
var oldVersion = result.Version;
104+
await (transaction.CommitAsync());
105+
session.Clear();
106+
107+
Assert.That(
108+
(await (session.GetAsync<Entity>(_id))).Version,
109+
Is.EqualTo(_optimisticLock.GetValueOrDefault(true) ? oldVersion + 1 : oldVersion));
110+
}
111+
}
112+
}
113+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
3+
namespace NHibernate.Test.NHSpecificTest.GH3204
4+
{
5+
public class Entity
6+
{
7+
public virtual Guid Id { get; set; }
8+
public virtual string Name { get; set; }
9+
public virtual int Version { get; set; }
10+
public virtual Child OneToOne { get; set; }
11+
}
12+
13+
public class Child
14+
{
15+
public virtual Guid Id { get; set; }
16+
public virtual string Name { get; set; }
17+
public virtual Entity Parent { get; set; }
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
using System;
2+
using NHibernate.Cfg.MappingSchema;
3+
using NHibernate.Mapping.ByCode;
4+
using NUnit.Framework;
5+
6+
namespace NHibernate.Test.NHSpecificTest.GH3204
7+
{
8+
[TestFixture(true)]
9+
[TestFixture(false)]
10+
[TestFixture(null)]
11+
public class OneToOneOptimisticLockFixture : TestCaseMappingByCode
12+
{
13+
private readonly bool? _optimisticLock;
14+
private Guid _id;
15+
16+
public OneToOneOptimisticLockFixture(bool? optimisticLock)
17+
{
18+
_optimisticLock = optimisticLock;
19+
}
20+
21+
protected override HbmMapping GetMappings()
22+
{
23+
var mapper = new ModelMapper();
24+
mapper.Class<Entity>(
25+
rc =>
26+
{
27+
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
28+
rc.Property(x => x.Name);
29+
rc.Version(x => x.Version, m => { });
30+
rc.OneToOne(
31+
x => x.OneToOne,
32+
m =>
33+
{
34+
m.PropertyReference(x => x.Parent);
35+
m.ForeignKey("none");
36+
m.Cascade(Mapping.ByCode.Cascade.All);
37+
if (_optimisticLock.HasValue)
38+
m.OptimisticLock(_optimisticLock.Value);
39+
});
40+
});
41+
42+
mapper.Class<Child>(
43+
rc =>
44+
{
45+
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
46+
rc.Property(x => x.Name);
47+
rc.ManyToOne(
48+
x => x.Parent,
49+
m =>
50+
{
51+
m.Unique(true);
52+
m.ForeignKey("none");
53+
});
54+
});
55+
56+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
57+
}
58+
59+
protected override void OnSetUp()
60+
{
61+
using (var session = OpenSession())
62+
using (var transaction = session.BeginTransaction())
63+
{
64+
var e1 = new Entity { Name = "Bob" };
65+
session.Save(e1);
66+
transaction.Commit();
67+
_id = e1.Id;
68+
}
69+
}
70+
71+
protected override void OnTearDown()
72+
{
73+
using (var session = OpenSession())
74+
using (var transaction = session.BeginTransaction())
75+
{
76+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
77+
78+
transaction.Commit();
79+
}
80+
}
81+
82+
[Test]
83+
public void Get()
84+
{
85+
using (var session = OpenSession())
86+
using (var transaction = session.BeginTransaction())
87+
{
88+
var result = session.Get<Entity>(_id);
89+
90+
var child = new Child() { Parent = result };
91+
result.OneToOne = child;
92+
var oldVersion = result.Version;
93+
transaction.Commit();
94+
session.Clear();
95+
96+
Assert.That(
97+
session.Get<Entity>(_id).Version,
98+
Is.EqualTo(_optimisticLock.GetValueOrDefault(true) ? oldVersion + 1 : oldVersion));
99+
}
100+
}
101+
}
102+
}

src/NHibernate/Cfg/MappingSchema/Hbm.generated.cs

+6
Original file line numberDiff line numberDiff line change
@@ -2922,6 +2922,11 @@ public partial class HbmOneToOne {
29222922
[System.ComponentModel.DefaultValueAttribute(false)]
29232923
public bool constrained;
29242924

2925+
/// <remarks/>
2926+
[System.Xml.Serialization.XmlAttributeAttribute("optimistic-lock")]
2927+
[System.ComponentModel.DefaultValueAttribute(true)]
2928+
public bool optimisticlock;
2929+
29252930
/// <remarks/>
29262931
[System.Xml.Serialization.XmlAttributeAttribute("foreign-key")]
29272932
public string foreignkey;
@@ -2944,6 +2949,7 @@ public partial class HbmOneToOne {
29442949

29452950
public HbmOneToOne() {
29462951
this.constrained = false;
2952+
this.optimisticlock = true;
29472953
}
29482954
}
29492955

src/NHibernate/Cfg/MappingSchema/HbmOneToOne.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ public string Access
2020

2121
public bool OptimisticLock
2222
{
23-
get { return true; }
23+
get => optimisticlock;
24+
set => optimisticlock = value;
2425
}
2526

2627
#endregion

src/NHibernate/Mapping/ByCode/Impl/OneToOneMapper.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public void Access(System.Type accessorType)
5555

5656
public void OptimisticLock(bool takeInConsiderationForOptimisticLock)
5757
{
58-
// not supported by HbmOneToOne
58+
_oneToOne.optimisticlock = takeInConsiderationForOptimisticLock;
5959
}
6060

6161
#endregion

src/NHibernate/nhibernate-mapping.xsd

+1
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,7 @@
10401040
</xs:attribute>
10411041
<xs:attribute name="constrained" default="false" type="xs:boolean">
10421042
</xs:attribute>
1043+
<xs:attribute name="optimistic-lock" default="true" type="xs:boolean" />
10431044
<xs:attribute name="foreign-key" type="xs:string" />
10441045
<xs:attribute name="property-ref" type="xs:string" />
10451046
<xs:attribute name="lazy" type="laziness">

0 commit comments

Comments
 (0)