Skip to content

Commit 63b226e

Browse files
Fix nhibernate#2608 - persisting a one-to-one with delayed insert fails (nhibernate#2609)
Co-authored-by: Roman Artiukhin <[email protected]>
1 parent e492709 commit 63b226e

File tree

15 files changed

+231
-7
lines changed

15 files changed

+231
-7
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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 NUnit.Framework;
12+
13+
namespace NHibernate.Test.NHSpecificTest.GH2608
14+
{
15+
using System.Threading.Tasks;
16+
[TestFixture]
17+
public class FixtureAsync : BugTestCase
18+
{
19+
protected override void OnTearDown()
20+
{
21+
using (var session = OpenSession())
22+
using (var transaction = session.BeginTransaction())
23+
{
24+
session.CreateQuery("delete from PersonalDetails").ExecuteUpdate();
25+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
26+
27+
transaction.Commit();
28+
}
29+
}
30+
31+
[Test]
32+
public async Task MergeBidiPrimaryKeyOneToOneAsync()
33+
{
34+
using (var s = OpenSession())
35+
using (var tx = s.BeginTransaction())
36+
{
37+
var p = new Person { Name = "steve" };
38+
p.Details = new PersonalDetails { SomePersonalDetail = "I have big feet", Person = p };
39+
await (s.MergeAsync(p));
40+
await (tx.CommitAsync());
41+
}
42+
}
43+
44+
[Test]
45+
public async Task PersistBidiPrimaryKeyOneToOneAsync()
46+
{
47+
using (var s = OpenSession())
48+
using (var tx = s.BeginTransaction())
49+
{
50+
var p = new Person { Name = "steve" };
51+
p.Details = new PersonalDetails { SomePersonalDetail = "I have big feet", Person = p };
52+
await (s.PersistAsync(p));
53+
await (tx.CommitAsync());
54+
}
55+
}
56+
}
57+
}

src/NHibernate.Test/Async/Operations/MergeFixture.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ protected override void OnTearDown()
5050
await (s.DeleteAsync("from Competition", cancellationToken));
5151

5252
await (s.DeleteAsync("from Employer", cancellationToken));
53+
await (s.DeleteAsync("from Address", cancellationToken));
54+
await (s.DeleteAsync("from Person", cancellationToken));
5355

5456
await (tx.CommitAsync(cancellationToken));
5557
}
@@ -76,6 +78,8 @@ private void Cleanup()
7678
s.Delete("from Competition");
7779

7880
s.Delete("from Employer");
81+
s.Delete("from Address");
82+
s.Delete("from Person");
7983

8084
tx.Commit();
8185
}
@@ -156,6 +160,41 @@ public async Task MergeBidiForeignKeyOneToOneAsync()
156160
}
157161
}
158162

163+
[Test]
164+
public async Task MergeBidiPrimayKeyOneToOneAsync()
165+
{
166+
Person p;
167+
using (ISession s = OpenSession())
168+
using (ITransaction tx = s.BeginTransaction())
169+
{
170+
p = new Person {Name = "steve"};
171+
new PersonalDetails {SomePersonalDetail = "I have big feet", Person = p};
172+
await (s.PersistAsync(p));
173+
await (tx.CommitAsync());
174+
}
175+
176+
ClearCounts();
177+
178+
p.Details.SomePersonalDetail = p.Details.SomePersonalDetail + " and big hands too";
179+
using (ISession s = OpenSession())
180+
using (ITransaction tx = s.BeginTransaction())
181+
{
182+
p = (Person) await (s.MergeAsync(p));
183+
await (tx.CommitAsync());
184+
}
185+
186+
AssertInsertCount(0);
187+
AssertUpdateCount(1);
188+
AssertDeleteCount(0);
189+
190+
using (ISession s = OpenSession())
191+
using (ITransaction tx = s.BeginTransaction())
192+
{
193+
await (s.DeleteAsync(p));
194+
await (tx.CommitAsync());
195+
}
196+
}
197+
159198
[Test]
160199
public async Task MergeDeepTreeAsync()
161200
{
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using NUnit.Framework;
2+
3+
namespace NHibernate.Test.NHSpecificTest.GH2608
4+
{
5+
[TestFixture]
6+
public class Fixture : BugTestCase
7+
{
8+
protected override void OnTearDown()
9+
{
10+
using (var session = OpenSession())
11+
using (var transaction = session.BeginTransaction())
12+
{
13+
session.CreateQuery("delete from PersonalDetails").ExecuteUpdate();
14+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
15+
16+
transaction.Commit();
17+
}
18+
}
19+
20+
[Test]
21+
public void MergeBidiPrimaryKeyOneToOne()
22+
{
23+
using (var s = OpenSession())
24+
using (var tx = s.BeginTransaction())
25+
{
26+
var p = new Person { Name = "steve" };
27+
p.Details = new PersonalDetails { SomePersonalDetail = "I have big feet", Person = p };
28+
s.Merge(p);
29+
tx.Commit();
30+
}
31+
}
32+
33+
[Test]
34+
public void PersistBidiPrimaryKeyOneToOne()
35+
{
36+
using (var s = OpenSession())
37+
using (var tx = s.BeginTransaction())
38+
{
39+
var p = new Person { Name = "steve" };
40+
p.Details = new PersonalDetails { SomePersonalDetail = "I have big feet", Person = p };
41+
s.Persist(p);
42+
tx.Commit();
43+
}
44+
}
45+
}
46+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
3+
assembly="NHibernate.Test"
4+
namespace="NHibernate.Test.NHSpecificTest.GH2608">
5+
6+
<class name="Person">
7+
<id name="Id">
8+
<generator class="native"/>
9+
</id>
10+
<property name="Name"/>
11+
<one-to-one name="Details" class="PersonalDetails" cascade="all"/>
12+
</class>
13+
14+
<class name="PersonalDetails">
15+
<id name="Id">
16+
<generator class="foreign">
17+
<param name="property">Person</param>
18+
</generator>
19+
</id>
20+
<property name="SomePersonalDetail"/>
21+
<one-to-one name="Person" class="Person" constrained="true"/>
22+
</class>
23+
24+
</hibernate-mapping>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace NHibernate.Test.NHSpecificTest.GH2608
2+
{
3+
public class Person
4+
{
5+
public virtual long Id { get; set; }
6+
public virtual string Name { get; set; }
7+
public virtual PersonalDetails Details { get; set; }
8+
}
9+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace NHibernate.Test.NHSpecificTest.GH2608
2+
{
3+
public class PersonalDetails
4+
{
5+
public virtual long Id { get; set; }
6+
public virtual string SomePersonalDetail { get; set; }
7+
8+
public virtual Person Person { get; set; }
9+
}
10+
}

src/NHibernate.Test/Operations/MergeFixture.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ private void Cleanup()
3838
s.Delete("from Competition");
3939

4040
s.Delete("from Employer");
41+
s.Delete("from Address");
42+
s.Delete("from Person");
4143

4244
tx.Commit();
4345
}
@@ -118,7 +120,7 @@ public void MergeBidiForeignKeyOneToOne()
118120
}
119121
}
120122

121-
[Test, Ignore("Need some more investigation about id sync.")]
123+
[Test]
122124
public void MergeBidiPrimayKeyOneToOne()
123125
{
124126
Person p;

src/NHibernate.Test/Operations/OneToOne.hbm.xml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@
3232

3333
<class name="PersonalDetails" table="OPS_PERS_DETAIL">
3434
<id name="Id" column="ID" type="long">
35-
<generator class="increment"/>
35+
<generator class="foreign">
36+
<param name="property">Person</param>
37+
</generator>
3638
</id>
3739
<property name="SomePersonalDetail" column="SOME_DETAIL" type="string"/>
3840
<one-to-one name="Person" class="Person" constrained="true" />
3941
</class>
4042

41-
</hibernate-mapping>
43+
</hibernate-mapping>

src/NHibernate/Action/DelayedPostInsertIdentifier.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,10 @@ public override string ToString()
4545
{
4646
return string.Format("<delayed:{0}>", sequence);
4747
}
48+
49+
/// <summary>
50+
/// The actual identifier value that has been generated.
51+
/// </summary>
52+
public object ActualId { get; set; }
4853
}
4954
}

src/NHibernate/Action/EntityInsertAction.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,19 @@ public override void Execute()
4444

4545
bool veto = PreInsert();
4646

47+
var wasDelayed = false;
4748
// Don't need to lock the cache here, since if someone
4849
// else inserted the same pk first, the insert would fail
4950
if (!veto)
5051
{
52+
// The identifier may be a foreign delayed identifier, which at this point should have been resolved.
53+
if (id is DelayedPostInsertIdentifier delayed)
54+
{
55+
wasDelayed = true;
56+
id = delayed.ActualId ??
57+
throw new InvalidOperationException(
58+
$"The delayed foreign identifier {delayed} has not been resolved before insertion of a {instance}");
59+
}
5160
persister.Insert(id, State, instance, Session);
5261

5362
EntityEntry entry = Session.PersistenceContext.GetEntry(instance);
@@ -57,6 +66,10 @@ public override void Execute()
5766
}
5867

5968
entry.PostInsert();
69+
if (wasDelayed)
70+
{
71+
Session.PersistenceContext.ReplaceDelayedEntityIdentityInsertKeys(entry.EntityKey, id);
72+
}
6073

6174
if (persister.HasInsertGeneratedProperties)
6275
{

0 commit comments

Comments
 (0)