Skip to content

Commit b9c88f3

Browse files
Support IdBag in select id generator
Co-Authored-by: Frédéric Delaporte <[email protected]>
1 parent 3ad3592 commit b9c88f3

File tree

9 files changed

+243
-13
lines changed

9 files changed

+243
-13
lines changed

src/NHibernate.Test/Async/NHSpecificTest/NH3150/SelectGeneratorFixture.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
//------------------------------------------------------------------------------
99

1010

11+
using System.Collections.Generic;
1112
using NHibernate.Dialect;
1213
using NUnit.Framework;
1314

@@ -28,6 +29,8 @@ protected override void OnTearDown()
2829
using (var s = OpenSession())
2930
using (var tx = s.BeginTransaction())
3031
{
32+
// Delete loads in memory and handle the cascade.
33+
s.Delete("from Worker2");
3134
s.CreateQuery("delete from System.Object").ExecuteUpdate();
3235
tx.Commit();
3336
}
@@ -117,5 +120,34 @@ public async Task CanUseComponentAsNaturalIdAsync()
117120
Assert.That(worker2.Id, Is.EqualTo(2), "Id of second worker should be 2");
118121
}
119122
}
123+
124+
[Test]
125+
public async Task IdBagWithSelectPOIDAsync()
126+
{
127+
int workerId;
128+
129+
using (var s = OpenSession())
130+
using (var tx = s.BeginTransaction())
131+
{
132+
var worker = new Worker2();
133+
var role = new Role { Description = "keeper" };
134+
135+
worker.Roles = new List<Role>() { role };
136+
137+
await (s.SaveAsync(worker));
138+
await (s.SaveAsync(role));
139+
140+
await (tx.CommitAsync());
141+
142+
workerId = worker.Id;
143+
}
144+
145+
using (var s = OpenSession())
146+
{
147+
var saved_worker = await (s.GetAsync<Worker2>(workerId));
148+
Assert.That(saved_worker.Roles, Is.Not.Null, "roles should not be null");
149+
Assert.That(saved_worker.Roles.Count, Is.EqualTo(1), "roles count should be 1");
150+
}
151+
}
120152
}
121153
}
Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
1-
namespace NHibernate.Test.NHSpecificTest.NH3150
1+
using System.Collections.Generic;
2+
3+
namespace NHibernate.Test.NHSpecificTest.NH3150
24
{
35
public class Worker
46
{
5-
public virtual int? Id { get; set; }
7+
public virtual int Id { get; set; }
68

79
public virtual string Name { get; set; }
810
public virtual string Position { get; set; }
911
}
1012

1113
public class WorkerWithExplicitKey
1214
{
13-
public virtual int? Id { get; set; }
15+
public virtual int Id { get; set; }
1416

1517
public virtual string Name { get; set; }
1618
public virtual string Position { get; set; }
1719
}
1820

1921
public class WorkerWithComponent
2022
{
21-
public virtual int? Id { get; set; }
23+
public virtual int Id { get; set; }
2224
public virtual NidComponent Nid { get; set; }
2325

2426
public class NidComponent
@@ -28,4 +30,16 @@ public class NidComponent
2830
// No need to implement Equals for what the test does.
2931
}
3032
}
33+
34+
public class Worker2
35+
{
36+
public virtual int Id { get; set; }
37+
public virtual IList<Role> Roles { get; set; }
38+
}
39+
40+
public class Role
41+
{
42+
public virtual int Id { get; set; }
43+
public virtual string Description { get; set; }
44+
}
3145
}

src/NHibernate.Test/NHSpecificTest/NH3150/Mappings.hbm.xml

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8" ?>
1+
<?xml version="1.0" encoding="utf-8" ?>
22
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
33
namespace="NHibernate.Test.NHSpecificTest.NH3150"
44
assembly="NHibernate.Test">
@@ -37,6 +37,27 @@
3737
</natural-id>
3838
</class>
3939

40+
<class name="Worker2">
41+
<id name="Id">
42+
<generator class="identity"/>
43+
</id>
44+
45+
<idbag name="Roles" inverse="false" cascade="all-delete-orphan" table="workerRoles">
46+
<collection-id column="id" type="Int32">
47+
<generator class="select"/>
48+
</collection-id>
49+
<key column="worker_id"/>
50+
<many-to-many column="role_id" class="Role" fetch="join"/>
51+
</idbag>
52+
</class>
53+
54+
<class name="Role">
55+
<id name="Id">
56+
<generator class="identity"/>
57+
</id>
58+
<property name="Description"/>
59+
</class>
60+
4061
<database-object>
4162
<create>
4263
CREATE TRIGGER dbo.id_gen_Worker ON dbo.Worker
@@ -105,4 +126,28 @@
105126
DROP TRIGGER dbo.id_gen_WorkerWithComponent;
106127
</drop>
107128
</database-object>
129+
130+
<database-object>
131+
<create>
132+
CREATE TRIGGER dbo.id_gen_workerRoles ON dbo.workerRoles
133+
INSTEAD OF INSERT
134+
AS
135+
BEGIN
136+
SET NOCOUNT ON;
137+
138+
declare @lastval int
139+
set @lastval = (select max(id) from workerRoles)
140+
if @lastval is null set @lastval = 0
141+
142+
SELECT * INTO #Inserted FROM Inserted
143+
UPDATE #Inserted set id = @lastval+1
144+
SET NOCOUNT OFF;
145+
INSERT INTO workerRoles SELECT * FROM #Inserted
146+
END
147+
GO
148+
</create>
149+
<drop>
150+
DROP TRIGGER dbo.id_gen_workerRoles;
151+
</drop>
152+
</database-object>
108153
</hibernate-mapping>

src/NHibernate.Test/NHSpecificTest/NH3150/SelectGeneratorFixture.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Collections.Generic;
12
using NHibernate.Dialect;
23
using NUnit.Framework;
34

@@ -17,6 +18,8 @@ protected override void OnTearDown()
1718
using (var s = OpenSession())
1819
using (var tx = s.BeginTransaction())
1920
{
21+
// Delete loads in memory and handle the cascade.
22+
s.Delete("from Worker2");
2023
s.CreateQuery("delete from System.Object").ExecuteUpdate();
2124
tx.Commit();
2225
}
@@ -106,5 +109,34 @@ public void CanUseComponentAsNaturalId()
106109
Assert.That(worker2.Id, Is.EqualTo(2), "Id of second worker should be 2");
107110
}
108111
}
112+
113+
[Test]
114+
public void IdBagWithSelectPOID()
115+
{
116+
int workerId;
117+
118+
using (var s = OpenSession())
119+
using (var tx = s.BeginTransaction())
120+
{
121+
var worker = new Worker2();
122+
var role = new Role { Description = "keeper" };
123+
124+
worker.Roles = new List<Role>() { role };
125+
126+
s.Save(worker);
127+
s.Save(role);
128+
129+
tx.Commit();
130+
131+
workerId = worker.Id;
132+
}
133+
134+
using (var s = OpenSession())
135+
{
136+
var saved_worker = s.Get<Worker2>(workerId);
137+
Assert.That(saved_worker.Roles, Is.Not.Null, "roles should not be null");
138+
Assert.That(saved_worker.Roles.Count, Is.EqualTo(1), "roles count should be 1");
139+
}
140+
}
109141
}
110142
}

src/NHibernate/Async/Id/Insert/AbstractSelectingDelegate.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
//------------------------------------------------------------------------------
99

1010

11+
using System;
1112
using System.Data;
1213
using System.Data.Common;
1314
using NHibernate.Engine;
@@ -62,7 +63,7 @@ public async Task<object> PerformInsertAsync(SqlCommandInfo insertSql, ISessionI
6263
var idSelect = await (session.Batcher.PrepareCommandAsync(CommandType.Text, selectSql, ParametersTypes, cancellationToken)).ConfigureAwait(false);
6364
try
6465
{
65-
await (BindParametersAsync(session, idSelect, binder.Entity, cancellationToken)).ConfigureAwait(false);
66+
await (BindParametersAsync(session, idSelect, binder, cancellationToken)).ConfigureAwait(false);
6667
var rs = await (session.Batcher.ExecuteReaderAsync(idSelect, cancellationToken)).ConfigureAwait(false);
6768
try
6869
{
@@ -107,6 +108,8 @@ public async Task<object> PerformInsertAsync(SqlCommandInfo insertSql, ISessionI
107108
/// <param name="ps">The prepared <see cref="SelectSQL"/> command </param>
108109
/// <param name="entity">The entity being saved. </param>
109110
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
111+
// Since 5.2
112+
[Obsolete("Use or override BindParameters(ISessionImplementor session, DbCommand ps, IBinder binder) instead.")]
110113
protected internal virtual Task BindParametersAsync(ISessionImplementor session, DbCommand ps, object entity, CancellationToken cancellationToken)
111114
{
112115
if (cancellationToken.IsCancellationRequested)
@@ -118,10 +121,27 @@ protected internal virtual Task BindParametersAsync(ISessionImplementor session,
118121
BindParameters(session, ps, entity);
119122
return Task.CompletedTask;
120123
}
121-
catch (System.Exception ex)
124+
catch (Exception ex)
122125
{
123126
return Task.FromException<object>(ex);
124127
}
125128
}
129+
130+
/// <summary>Bind any required parameter values into the SQL command <see cref="SelectSQL"/>.</summary>
131+
/// <param name="session">The session.</param>
132+
/// <param name="ps">The prepared <see cref="SelectSQL"/> command.</param>
133+
/// <param name="binder">The binder for the entity or collection being saved.</param>
134+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
135+
protected internal virtual Task BindParametersAsync(ISessionImplementor session, DbCommand ps, IBinder binder, CancellationToken cancellationToken)
136+
{
137+
if (cancellationToken.IsCancellationRequested)
138+
{
139+
return Task.FromCanceled<object>(cancellationToken);
140+
}
141+
// 6.0 TODO: remove the call to the obsoleted method.
142+
#pragma warning disable 618
143+
return BindParametersAsync(session, ps, binder.Entity, cancellationToken);
144+
#pragma warning restore 618
145+
}
126146
}
127147
}

src/NHibernate/Async/Id/SelectGenerator.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
//------------------------------------------------------------------------------
99

1010

11+
using System;
1112
using System.Collections.Generic;
1213
using System.Data.Common;
1314
using System.Linq;
1415
using NHibernate.Engine;
1516
using NHibernate.Id.Insert;
17+
using NHibernate.Persister.Collection;
1618
using NHibernate.Persister.Entity;
1719
using NHibernate.SqlCommand;
1820
using NHibernate.SqlTypes;
@@ -30,6 +32,8 @@ public partial class SelectGenerator : AbstractPostInsertGenerator, IConfigurabl
3032
public partial class SelectGeneratorDelegate : AbstractSelectingDelegate
3133
{
3234

35+
// Since 5.2
36+
[Obsolete("Use or override BindParameters(ISessionImplementor session, DbCommand ps, IBinder binder) instead.")]
3337
protected internal override async Task BindParametersAsync(ISessionImplementor session, DbCommand ps, object entity, CancellationToken cancellationToken)
3438
{
3539
cancellationToken.ThrowIfCancellationRequested();
@@ -40,6 +44,32 @@ protected internal override async Task BindParametersAsync(ISessionImplementor s
4044
}
4145
}
4246

47+
protected internal override Task BindParametersAsync(ISessionImplementor session, DbCommand ps, IBinder binder, CancellationToken cancellationToken)
48+
{
49+
if (cancellationToken.IsCancellationRequested)
50+
{
51+
return Task.FromCanceled<object>(cancellationToken);
52+
}
53+
try
54+
{
55+
if (entityPersister != null)
56+
{
57+
// 6.0 TODO: inline the call.
58+
#pragma warning disable 618
59+
return BindParametersAsync(session, ps, binder.Entity, cancellationToken);
60+
#pragma warning restore 618
61+
}
62+
else
63+
{
64+
return binder.BindValuesAsync(ps, cancellationToken);
65+
}
66+
}
67+
catch (Exception ex)
68+
{
69+
return Task.FromException<object>(ex);
70+
}
71+
}
72+
4373
protected internal override async Task<object> GetResultAsync(ISessionImplementor session, DbDataReader rs, object entity, CancellationToken cancellationToken)
4474
{
4575
cancellationToken.ThrowIfCancellationRequested();

src/NHibernate/Id/Insert/AbstractSelectingDelegate.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Data;
23
using System.Data.Common;
34
using NHibernate.Engine;
@@ -62,7 +63,7 @@ public object PerformInsert(SqlCommandInfo insertSql, ISessionImplementor sessio
6263
var idSelect = session.Batcher.PrepareCommand(CommandType.Text, selectSql, ParametersTypes);
6364
try
6465
{
65-
BindParameters(session, idSelect, binder.Entity);
66+
BindParameters(session, idSelect, binder);
6667
var rs = session.Batcher.ExecuteReader(idSelect);
6768
try
6869
{
@@ -109,8 +110,22 @@ public object PerformInsert(SqlCommandInfo insertSql, ISessionImplementor sessio
109110
/// <param name="session">The session </param>
110111
/// <param name="ps">The prepared <see cref="SelectSQL"/> command </param>
111112
/// <param name="entity">The entity being saved. </param>
113+
// Since 5.2
114+
[Obsolete("Use or override BindParameters(ISessionImplementor session, DbCommand ps, IBinder binder) instead.")]
112115
protected internal virtual void BindParameters(ISessionImplementor session, DbCommand ps, object entity) { }
113116

117+
/// <summary>Bind any required parameter values into the SQL command <see cref="SelectSQL"/>.</summary>
118+
/// <param name="session">The session.</param>
119+
/// <param name="ps">The prepared <see cref="SelectSQL"/> command.</param>
120+
/// <param name="binder">The binder for the entity or collection being saved.</param>
121+
protected internal virtual void BindParameters(ISessionImplementor session, DbCommand ps, IBinder binder)
122+
{
123+
// 6.0 TODO: remove the call to the obsoleted method.
124+
#pragma warning disable 618
125+
BindParameters(session, ps, binder.Entity);
126+
#pragma warning restore 618
127+
}
128+
114129
#region NH Specific
115130

116131
/// <summary>

0 commit comments

Comments
 (0)