Skip to content

Commit 4949174

Browse files
authored
Use table group joins for many-to-many in Criteria and Entity loaders (#2687)
Possible breaking change: Default not-found behavior now works correctly for many-to-many in Criteria so now it throws ObjectNotFoundException exception on many-to-many Criteria fetch for not found records.
1 parent d6fd1dc commit 4949174

20 files changed

+581
-279
lines changed

src/NHibernate.Test/Async/NHSpecificTest/GH1994/Fixture.cs renamed to src/NHibernate.Test/Async/NHSpecificTest/GH1994/ManyToManyFilteredFixture.cs

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
using System.Linq;
1212
using NHibernate.Criterion;
13-
using NHibernate.Dialect;
1413
using NHibernate.Linq;
1514
using NHibernate.SqlCommand;
1615
using NHibernate.Transform;
@@ -20,7 +19,7 @@ namespace NHibernate.Test.NHSpecificTest.GH1994
2019
{
2120
using System.Threading.Tasks;
2221
[TestFixture]
23-
public class FixtureAsync : BugTestCase
22+
public class ManyToManyFilteredFixtureAsync : BugTestCase
2423
{
2524
protected override void OnSetUp()
2625
{
@@ -41,14 +40,7 @@ protected override void OnTearDown()
4140
using (var session = OpenSession())
4241
using (var transaction = session.BeginTransaction())
4342
{
44-
// The HQL delete does all the job inside the database without loading the entities, but it does
45-
// not handle delete order for avoiding violating constraints if any. Use
46-
// session.Delete("from System.Object");
47-
// instead if in need of having NHibernate ordering the deletes, but this will cause
48-
// loading the entities in the session.
49-
5043
session.Delete("from System.Object");
51-
5244
transaction.Commit();
5345
}
5446
}
@@ -70,9 +62,6 @@ public async Task TestUnfilteredLinqQueryAsync()
7062
[Test]
7163
public async Task TestFilteredByWhereCollectionLinqQueryAsync()
7264
{
73-
if(Dialect is PostgreSQLDialect)
74-
Assert.Ignore("Dialect doesn't support 0/1 to bool implicit cast");
75-
7665
using (var s = OpenSession())
7766
{
7867
var query = await (s.Query<Asset>()
@@ -150,5 +139,31 @@ public async Task TestQueryOverRestrictionWithClauseAsync()
150139
Assert.That(query[0].Documents.Count, Is.EqualTo(1), "filtered asset documents");
151140
}
152141
}
142+
143+
[Test]
144+
public async Task LazyLoadAsync()
145+
{
146+
using (var s = OpenSession())
147+
{
148+
var asset = await (s.Query<Asset>().FirstAsync());
149+
Assert.That(asset.Documents.Count, Is.EqualTo(2));
150+
Assert.That(asset.DocumentsBag.Count, Is.EqualTo(2));
151+
Assert.That(asset.DocumentsFiltered.Count, Is.EqualTo(1));
152+
}
153+
}
154+
155+
[Test]
156+
public async Task LazyLoadFilteredAsync()
157+
{
158+
using (var s = OpenSession())
159+
{
160+
s.EnableFilter("deletedFilter").SetParameter("deletedParam", false);
161+
162+
var asset = await (s.Query<Asset>().FirstAsync());
163+
Assert.That(asset.Documents.Count, Is.EqualTo(1));
164+
Assert.That(asset.DocumentsBag.Count, Is.EqualTo(1));
165+
Assert.That(asset.DocumentsFiltered.Count, Is.EqualTo(1));
166+
}
167+
}
153168
}
154169
}

src/NHibernate.Test/Async/NHSpecificTest/NH750/Fixture.cs renamed to src/NHibernate.Test/Async/NHSpecificTest/NH750/ManyToManyNotFoundIgnoreFixture.cs

Lines changed: 87 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -10,78 +10,72 @@
1010

1111
using System;
1212
using NHibernate.Cfg;
13+
using NHibernate.Criterion;
14+
using NHibernate.Transform;
1315
using NUnit.Framework;
1416

1517
namespace NHibernate.Test.NHSpecificTest.NH750
1618
{
1719
using System.Threading.Tasks;
1820
[TestFixture]
19-
public class FixtureAsync : BugTestCase
21+
public class ManyToManyNotFoundIgnoreFixtureAsync : BugTestCase
2022
{
21-
protected override void OnTearDown()
22-
{
23-
using (ISession s = Sfi.OpenSession())
24-
{
25-
s.Delete("from Device");
26-
s.Delete("from Drive");
27-
s.Flush();
28-
}
29-
}
23+
private int id1;
24+
private int id2;
3025

31-
protected override void Configure(Configuration configuration)
26+
protected override void OnSetUp()
3227
{
33-
configuration.SetProperty(Cfg.Environment.UseSecondLevelCache, "false");
34-
base.Configure(configuration);
35-
}
36-
37-
[Test]
38-
public async Task DeviceOfDriveAsync()
39-
{
40-
int[] dvSavedId = new int[2];
4128
Drive dr1 = new Drive("Drive 1");
4229
Drive dr2 = new Drive("Drive 2");
4330
Drive dr3 = new Drive("Drive 3");
4431
Device dv1 = new Device("Device 1");
4532
Device dv2 = new Device("Device 2");
46-
using (ISession s = Sfi.OpenSession())
33+
using (var s = Sfi.OpenSession())
34+
using (var t = s.BeginTransaction())
4735
{
48-
await (s.SaveAsync(dr1));
49-
await (s.SaveAsync(dr2));
50-
await (s.SaveAsync(dr3));
51-
dvSavedId[0] = (int) await (s.SaveAsync(dv1));
52-
dvSavedId[1] = (int) await (s.SaveAsync(dv2));
53-
await (s.FlushAsync());
36+
s.Save(dr1);
37+
s.Save(dr2);
38+
s.Save(dr3);
39+
dv1.Drives.Add(dr1);
40+
dv1.Drives.Add(dr2);
41+
dv2.Drives.Add(dr1);
42+
dv2.Drives.Add(dr3);
43+
44+
id1 = (int) s.Save(dv1);
45+
id2 = (int) s.Save(dv2);
46+
s.Flush();
47+
48+
s.Clear();
49+
s.Delete(dr3);
50+
t.Commit();
5451
}
52+
}
5553

56-
dv1.Drives.Add(dr1);
57-
dv1.Drives.Add(dr2);
58-
dv2.Drives.Add(dr1);
59-
dv2.Drives.Add(dr3);
54+
protected override void OnTearDown()
55+
{
6056
using (ISession s = Sfi.OpenSession())
57+
using (var t = s.BeginTransaction())
6158
{
62-
dvSavedId[0] = (int) await (s.SaveAsync(dv1));
63-
dvSavedId[1] = (int) await (s.SaveAsync(dv2));
64-
await (s.FlushAsync());
59+
s.Delete("from Device");
60+
s.Delete("from Drive");
61+
t.Commit();
6562
}
66-
dv1 = null;
67-
dv2 = null;
63+
}
64+
65+
[Test]
66+
public async Task DeviceOfDriveAsync()
67+
{
68+
Device dv1;
69+
Device dv2;
6870
using (ISession s = Sfi.OpenSession())
6971
{
70-
await (s.DeleteAsync(dr3));
71-
await (s.FlushAsync());
72-
dv1 = (Device) await (s.LoadAsync(typeof(Device), dvSavedId[0]));
73-
dv2 = (Device) await (s.LoadAsync(typeof(Device), dvSavedId[1]));
72+
dv1 = (Device) await (s.LoadAsync(typeof(Device), id1));
73+
dv2 = (Device) await (s.LoadAsync(typeof(Device), id2));
7474
}
75-
Assert.AreEqual(2, dv1.Drives.Count);
76-
// Verify one is missing
77-
Assert.AreEqual(1, dv2.Drives.Count);
78-
// Verify dv1 unchanged
79-
Assert.IsTrue(dv1.Drives.Contains(dr1));
80-
Assert.IsTrue(dv1.Drives.Contains(dr2));
8175

82-
// Verify dv2
83-
Assert.IsTrue(dv2.Drives.Contains(dr1));
84-
Assert.IsFalse(dv2.Drives.Contains(dr3));
76+
Assert.That(dv1.Drives, Has.Count.EqualTo(2).And.None.Null);
77+
// Verify one is missing
78+
Assert.That(dv2.Drives, Has.Count.EqualTo(1).And.None.Null);
8579

8680
//Make sure that flush didn't touch not-found="ignore" records for not modified collection
8781
using (var s = Sfi.OpenSession())
@@ -99,7 +93,7 @@ public async Task DeviceOfDriveAsync()
9993
using (var t = s.BeginTransaction())
10094
{
10195
dv2 = await (s.GetAsync<Device>(dv2.Id));
102-
dv2.Drives.Add(dr2);
96+
dv2.Drives.Add(dv1.Drives[1]);
10397
await (t.CommitAsync());
10498
}
10599

@@ -120,5 +114,49 @@ async Task VerifyResultAsync(int expectedInCollection, int expectedInDb, string
120114
}
121115
}
122116
}
117+
118+
[Test]
119+
public async Task QueryOverFetchAsync()
120+
{
121+
using (var s = OpenSession())
122+
{
123+
var dv2 = await (s.QueryOver<Device>()
124+
.Fetch(SelectMode.Fetch, x => x.Drives)
125+
.Where(Restrictions.IdEq(id2))
126+
.TransformUsing(Transformers.DistinctRootEntity)
127+
.SingleOrDefaultAsync());
128+
129+
Assert.That(NHibernateUtil.IsInitialized(dv2.Drives), Is.True);
130+
Assert.That(dv2.Drives, Has.Count.EqualTo(1).And.None.Null);
131+
}
132+
}
133+
134+
[Test]
135+
public async Task HqlFetchAsync()
136+
{
137+
using (var s = OpenSession())
138+
{
139+
var dv2 = await (s.CreateQuery("from Device d left join fetch d.Drives where d.id = :id")
140+
.SetResultTransformer(Transformers.DistinctRootEntity)
141+
.SetParameter("id", id2)
142+
.UniqueResultAsync<Device>());
143+
144+
Assert.That(NHibernateUtil.IsInitialized(dv2.Drives), Is.True);
145+
Assert.That(dv2.Drives, Has.Count.EqualTo(1).And.None.Null);
146+
}
147+
}
148+
149+
[Test]
150+
public async Task LazyLoadAsync()
151+
{
152+
using (var s = OpenSession())
153+
{
154+
var dv2 = await (s.GetAsync<Device>(id2));
155+
await (NHibernateUtil.InitializeAsync(dv2.Drives));
156+
157+
Assert.That(NHibernateUtil.IsInitialized(dv2.Drives), Is.True);
158+
Assert.That(dv2.Drives, Has.Count.EqualTo(1).And.None.Null);
159+
}
160+
}
123161
}
124162
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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.Criterion;
13+
using NHibernate.Linq;
14+
using NHibernate.Transform;
15+
using NUnit.Framework;
16+
17+
namespace NHibernate.Test.NHSpecificTest.NH750
18+
{
19+
using System.Threading.Tasks;
20+
[TestFixture]
21+
public class ManyToManyThrowsForNotFoundFixtureAsync : BugTestCase
22+
{
23+
private int _id;
24+
25+
protected override void OnSetUp()
26+
{
27+
using (var s = Sfi.OpenSession())
28+
using (var t = s.BeginTransaction())
29+
{
30+
Device dv = new Device("Device");
31+
Drive dr = new Drive("Drive");
32+
s.Save(dr);
33+
dv.DrivesNotIgnored.Add(dr);
34+
35+
_id = (int) s.Save(dv);
36+
s.Flush();
37+
38+
s.Clear();
39+
s.Delete(dr);
40+
t.Commit();
41+
}
42+
}
43+
44+
protected override void OnTearDown()
45+
{
46+
using (var s = OpenSession())
47+
using (var t = s.BeginTransaction())
48+
{
49+
s.Delete("from Device");
50+
s.Delete("from Drive");
51+
t.Commit();
52+
}
53+
}
54+
55+
[Test]
56+
public async Task LazyLoadAsync()
57+
{
58+
using var s = OpenSession();
59+
var device = await (s.GetAsync<Device>(_id));
60+
Assert.ThrowsAsync<ObjectNotFoundException>(() => NHibernateUtil.InitializeAsync(device.DrivesNotIgnored));
61+
}
62+
63+
[Test]
64+
public void QueryOverFetchAsync()
65+
{
66+
using var s = OpenSession();
67+
var queryOver = s.QueryOver<Device>()
68+
.Fetch(SelectMode.Fetch, x => x.DrivesNotIgnored)
69+
.Where(Restrictions.IdEq(_id))
70+
.TransformUsing(Transformers.DistinctRootEntity);
71+
Assert.ThrowsAsync<ObjectNotFoundException>(async () => await (NHibernateUtil.InitializeAsync(await (queryOver.SingleOrDefaultAsync()))));
72+
}
73+
74+
[Test]
75+
public void LinqFetchAsync()
76+
{
77+
using var s = OpenSession();
78+
var query = s.Query<Device>()
79+
80+
.Fetch(x => x.DrivesNotIgnored)
81+
.Where(x => x.Id == _id);
82+
Assert.ThrowsAsync<ObjectNotFoundException>(async () => await (NHibernateUtil.InitializeAsync(await (query.SingleOrDefaultAsync()))));
83+
}
84+
}
85+
}

src/NHibernate.Test/Async/PropertyRef/ManyToManyPropertyRefFixture.cs

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

1010

11+
using System.Linq;
1112
using NHibernate.Criterion;
13+
using NHibernate.Linq;
1214
using NUnit.Framework;
1315

1416
namespace NHibernate.Test.PropertyRef
1517
{
1618
using System.Threading.Tasks;
17-
[TestFixture]
19+
[TestFixture(Description = "NH-2180 (GH-1214)")]
1820
public class ManyToManyPropertyRefFixtureAsync : TestCase
1921
{
2022
protected override string[] Mappings => new[] { "PropertyRef.ManyToManyWithPropertyRef.hbm.xml" };
2123

2224
protected override string MappingsAssembly => "NHibernate.Test";
2325

24-
private object _manyAId;
26+
private long _manyAId;
2527

2628
protected override void OnSetUp()
2729
{
@@ -34,7 +36,7 @@ protected override void OnSetUp()
3436
var manyB2 = new ManyB { Number = 8, Value = "a value of b2" };
3537
var manyB3 = new ManyB { Number = 12, Value = "a value of b3" };
3638

37-
_manyAId = session.Save(manyA);
39+
_manyAId = (long) session.Save(manyA);
3840
session.Save(manyB1);
3941
session.Save(manyB2);
4042
session.Save(manyB3);
@@ -144,5 +146,20 @@ bei NHibernate.Type.EntityType.LoadByUniqueKey(String entityName, String uniqueK
144146

145147
Assert.That(loadedManyA.ManyBs, Has.Count.EqualTo(3).And.None.Null);
146148
}
149+
150+
[Test]
151+
public async Task LinqFetchAsync()
152+
{
153+
using (var session = OpenSession())
154+
{
155+
var manyA = (await (session
156+
.Query<ManyA>()
157+
.Where(a => a.Id == _manyAId)
158+
.FetchMany(a => a.ManyBs)
159+
.ToListAsync()))
160+
.First();
161+
Assert.That(manyA.ManyBs, Has.Count.EqualTo(3).And.None.Null);
162+
}
163+
}
147164
}
148165
}

0 commit comments

Comments
 (0)