Skip to content

Commit 58b6c04

Browse files
committed
Fix nullable entity comparison with null
1 parent 7efd8f1 commit 58b6c04

File tree

4 files changed

+178
-48
lines changed

4 files changed

+178
-48
lines changed

src/NHibernate.Test/Async/Hql/EntityJoinHqlTest.cs

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

1010

11+
using System.Linq;
1112
using System.Text.RegularExpressions;
1213
using NHibernate.Cfg.MappingSchema;
1314
using NHibernate.Mapping.ByCode;
1415
using NHibernate.Test.Hql.EntityJoinHqlTestEntities;
1516
using NUnit.Framework;
17+
using NHibernate.Linq;
1618

1719
namespace NHibernate.Test.Hql
1820
{
@@ -164,12 +166,12 @@ public async Task EntityJoinWithNullableOneToOneEntityComparisonInWithClausShoul
164166
.CreateQuery(
165167
"select ex "
166168
+ "from NullableOwner ex "
167-
+ "left join OneToOneEntity st with st = ex.OneToOne "
168-
).SetMaxResults(1)
169+
+ "inner join OneToOneEntity st with st = ex.OneToOne "
170+
)
169171
.UniqueResultAsync<NullableOwner>());
170172

171-
Assert.That(Regex.Matches(sqlLog.GetWholeLog(), "OneToOneEntity").Count, Is.EqualTo(2));
172-
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
173+
Assert.That(entity, Is.Not.Null);
174+
Assert.That(Regex.Matches(sqlLog.Appender.GetEvents()[0].RenderedMessage, "OneToOneEntity").Count, Is.EqualTo(2));
173175
}
174176
}
175177

@@ -188,8 +190,28 @@ public async Task NullableOneToOneWhereEntityIsNotNullAsync()
188190
).SetMaxResults(1)
189191
.UniqueResultAsync<NullableOwner>());
190192

191-
Assert.That(Regex.Matches(sqlLog.GetWholeLog(), "OneToOneEntity").Count, Is.EqualTo(1));
192-
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
193+
Assert.That(entity, Is.Not.Null);
194+
Assert.That(Regex.Matches(sqlLog.Appender.GetEvents()[0].RenderedMessage, "OneToOneEntity").Count, Is.EqualTo(1));
195+
}
196+
}
197+
198+
[Test]
199+
public async Task NullableOneToOneWhereEntityIsNullAsync()
200+
{
201+
using (var sqlLog = new SqlLogSpy())
202+
using (var session = OpenSession())
203+
{
204+
var entity =
205+
await (session
206+
.CreateQuery(
207+
"select ex "
208+
+ "from NullableOwner ex "
209+
+ "where ex.OneToOne is null "
210+
).SetMaxResults(1)
211+
.UniqueResultAsync<NullableOwner>());
212+
213+
Assert.That(entity, Is.Not.Null);
214+
Assert.That(Regex.Matches(sqlLog.Appender.GetEvents()[0].RenderedMessage, "OneToOneEntity").Count, Is.EqualTo(1));
193215
}
194216
}
195217

@@ -208,8 +230,8 @@ public async Task NullableOneToOneWhereIdIsNotNullAsync()
208230
).SetMaxResults(1)
209231
.UniqueResultAsync<NullableOwner>());
210232

211-
Assert.That(Regex.Matches(sqlLog.GetWholeLog(), "OneToOneEntity").Count, Is.EqualTo(1));
212-
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
233+
Assert.That(entity, Is.Not.Null);
234+
Assert.That(Regex.Matches(sqlLog.Appender.GetEvents()[0].RenderedMessage, "OneToOneEntity").Count, Is.EqualTo(1));
213235
}
214236
}
215237

@@ -222,14 +244,32 @@ public async Task NullablePropRefWhereIdEntityNotNullShouldAddJoinAsync()
222244
var entity =
223245
await (session
224246
.CreateQuery(
225-
"select ex "
226-
+ "from NullableOwner ex "
247+
"select ex from NullableOwner ex "
227248
+ "where ex.PropRef is not null "
228-
).SetMaxResults(1)
249+
)
229250
.UniqueResultAsync<NullableOwner>());
230251

231-
Assert.That(Regex.Matches(sqlLog.GetWholeLog(), "PropRefEntity").Count, Is.EqualTo(1));
232-
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
252+
Assert.That(entity, Is.Not.Null);
253+
Assert.That(Regex.Matches(sqlLog.Appender.GetEvents()[0].RenderedMessage, "PropRefEntity").Count, Is.EqualTo(1));
254+
}
255+
}
256+
257+
[Test]
258+
public async Task NullablePropRefWhereIdEntityNullShouldAddJoinAsync()
259+
{
260+
using (var sqlLog = new SqlLogSpy())
261+
using (var session = OpenSession())
262+
{
263+
var entity =
264+
await (session
265+
.CreateQuery(
266+
"select ex from NullableOwner ex "
267+
+ "where ex.PropRef is null "
268+
)
269+
.UniqueResultAsync<NullableOwner>());
270+
271+
Assert.That(entity, Is.Not.Null);
272+
Assert.That(Regex.Matches(sqlLog.Appender.GetEvents()[0].RenderedMessage, "PropRefEntity").Count, Is.EqualTo(1));
233273
}
234274
}
235275

@@ -245,28 +285,41 @@ public async Task NullableOneToOneFetchQueryIsNotAffectedAsync()
245285
"select ex "
246286
+ "from NullableOwner ex left join fetch ex.OneToOne o "
247287
+ "where o is null "
248-
).SetMaxResults(1)
288+
)
249289
.UniqueResultAsync<NullableOwner>());
250290

291+
Assert.That(entity, Is.Not.Null);
251292
Assert.That(Regex.Matches(sqlLog.GetWholeLog(), "OneToOneEntity").Count, Is.EqualTo(1));
252293
}
253294
}
254-
295+
296+
[Test(Description = "GH-2611")]
297+
public async Task NullableOneIsNullLinqAsync()
298+
{
299+
using (var session = OpenSession())
300+
{
301+
var entity = await (session.Query<NullableOwner>().Where(x => x.OneToOne == null).FirstOrDefaultAsync());
302+
Assert.That(entity, Is.Not.Null);
303+
}
304+
}
305+
255306
[Test]
256307
public async Task NullableOneToOneFetchQueryIsNotAffected2Async()
257308
{
258309
using (var sqlLog = new SqlLogSpy())
259310
using (var session = OpenSession())
260311
{
261-
var entity =
312+
var entities =
262313
await (session
263314
.CreateQuery(
264315
"select ex "
265316
+ "from NullableOwner ex left join fetch ex.OneToOne o "
266317
+ "where o.Id is null "
267-
).SetMaxResults(1)
268-
.UniqueResultAsync<NullableOwner>());
318+
)
319+
.ListAsync<NullableOwner>());
320+
var entity = entities[0];
269321

322+
Assert.That(entity, Is.Not.Null);
270323
Assert.That(Regex.Matches(sqlLog.GetWholeLog(), "OneToOneEntity").Count, Is.EqualTo(1));
271324
}
272325
}
@@ -488,8 +541,9 @@ protected override HbmMapping GetMappings()
488541
mapper.Class<OneToOneEntity>(
489542
rc =>
490543
{
491-
rc.Id(e => e.Id, m => m.Generator(Generators.GuidComb));
544+
rc.Id(e => e.Id, m => m.Generator(Generators.Foreign<OneToOneEntity>(x => x.Parent)));
492545
rc.Property(e => e.Name);
546+
rc.OneToOne(x => x.Parent, x => x.Constrained(true));
493547
});
494548

495549
mapper.Class<PropRefEntity>(
@@ -505,7 +559,11 @@ protected override HbmMapping GetMappings()
505559
{
506560
rc.Id(e => e.Id, m => m.Generator(Generators.GuidComb));
507561
rc.Property(e => e.Name);
508-
rc.OneToOne(e => e.OneToOne, m => m.Constrained(false));
562+
rc.OneToOne(e => e.OneToOne, m =>
563+
{
564+
m.Constrained(false);
565+
m.Cascade(Mapping.ByCode.Cascade.All);
566+
});
509567
rc.ManyToOne(
510568
e => e.PropRef,
511569
m =>
@@ -514,6 +572,7 @@ protected override HbmMapping GetMappings()
514572
m.PropertyRef(nameof(PropRefEntity.PropertyRef));
515573
m.ForeignKey("none");
516574
m.NotFound(NotFoundMode.Ignore);
575+
m.Cascade(Mapping.ByCode.Cascade.All);
517576
});
518577
});
519578

@@ -584,7 +643,10 @@ protected override void OnSetUp()
584643
};
585644
session.Save(_noAssociation);
586645

587-
session.Flush();
646+
session.Save(new NullableOwner() {Name = "NoAssociation"});
647+
var nullableOwner = new NullableOwner() {Name = "Association", PropRef = new PropRefEntity() {Name = "x", PropertyRef = "xx"}, OneToOne = new OneToOneEntity() {Name = "x"},};
648+
nullableOwner.OneToOne.Parent = nullableOwner;
649+
session.Save(nullableOwner);
588650
transaction.Commit();
589651
}
590652
}

src/NHibernate.Test/Hql/EntityJoinHqlTest.cs

Lines changed: 83 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Text.RegularExpressions;
1+
using System.Linq;
2+
using System.Text.RegularExpressions;
23
using NHibernate.Cfg.MappingSchema;
34
using NHibernate.Mapping.ByCode;
45
using NHibernate.Test.Hql.EntityJoinHqlTestEntities;
@@ -153,12 +154,12 @@ public void EntityJoinWithNullableOneToOneEntityComparisonInWithClausShouldAddJo
153154
.CreateQuery(
154155
"select ex "
155156
+ "from NullableOwner ex "
156-
+ "left join OneToOneEntity st with st = ex.OneToOne "
157-
).SetMaxResults(1)
157+
+ "inner join OneToOneEntity st with st = ex.OneToOne "
158+
)
158159
.UniqueResult<NullableOwner>();
159160

160-
Assert.That(Regex.Matches(sqlLog.GetWholeLog(), "OneToOneEntity").Count, Is.EqualTo(2));
161-
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
161+
Assert.That(entity, Is.Not.Null);
162+
Assert.That(Regex.Matches(sqlLog.Appender.GetEvents()[0].RenderedMessage, "OneToOneEntity").Count, Is.EqualTo(2));
162163
}
163164
}
164165

@@ -177,8 +178,28 @@ public void NullableOneToOneWhereEntityIsNotNull()
177178
).SetMaxResults(1)
178179
.UniqueResult<NullableOwner>();
179180

180-
Assert.That(Regex.Matches(sqlLog.GetWholeLog(), "OneToOneEntity").Count, Is.EqualTo(1));
181-
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
181+
Assert.That(entity, Is.Not.Null);
182+
Assert.That(Regex.Matches(sqlLog.Appender.GetEvents()[0].RenderedMessage, "OneToOneEntity").Count, Is.EqualTo(1));
183+
}
184+
}
185+
186+
[Test]
187+
public void NullableOneToOneWhereEntityIsNull()
188+
{
189+
using (var sqlLog = new SqlLogSpy())
190+
using (var session = OpenSession())
191+
{
192+
var entity =
193+
session
194+
.CreateQuery(
195+
"select ex "
196+
+ "from NullableOwner ex "
197+
+ "where ex.OneToOne is null "
198+
).SetMaxResults(1)
199+
.UniqueResult<NullableOwner>();
200+
201+
Assert.That(entity, Is.Not.Null);
202+
Assert.That(Regex.Matches(sqlLog.Appender.GetEvents()[0].RenderedMessage, "OneToOneEntity").Count, Is.EqualTo(1));
182203
}
183204
}
184205

@@ -197,8 +218,8 @@ public void NullableOneToOneWhereIdIsNotNull()
197218
).SetMaxResults(1)
198219
.UniqueResult<NullableOwner>();
199220

200-
Assert.That(Regex.Matches(sqlLog.GetWholeLog(), "OneToOneEntity").Count, Is.EqualTo(1));
201-
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
221+
Assert.That(entity, Is.Not.Null);
222+
Assert.That(Regex.Matches(sqlLog.Appender.GetEvents()[0].RenderedMessage, "OneToOneEntity").Count, Is.EqualTo(1));
202223
}
203224
}
204225

@@ -211,14 +232,32 @@ public void NullablePropRefWhereIdEntityNotNullShouldAddJoin()
211232
var entity =
212233
session
213234
.CreateQuery(
214-
"select ex "
215-
+ "from NullableOwner ex "
235+
"select ex from NullableOwner ex "
216236
+ "where ex.PropRef is not null "
217-
).SetMaxResults(1)
237+
)
218238
.UniqueResult<NullableOwner>();
219239

220-
Assert.That(Regex.Matches(sqlLog.GetWholeLog(), "PropRefEntity").Count, Is.EqualTo(1));
221-
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
240+
Assert.That(entity, Is.Not.Null);
241+
Assert.That(Regex.Matches(sqlLog.Appender.GetEvents()[0].RenderedMessage, "PropRefEntity").Count, Is.EqualTo(1));
242+
}
243+
}
244+
245+
[Test]
246+
public void NullablePropRefWhereIdEntityNullShouldAddJoin()
247+
{
248+
using (var sqlLog = new SqlLogSpy())
249+
using (var session = OpenSession())
250+
{
251+
var entity =
252+
session
253+
.CreateQuery(
254+
"select ex from NullableOwner ex "
255+
+ "where ex.PropRef is null "
256+
)
257+
.UniqueResult<NullableOwner>();
258+
259+
Assert.That(entity, Is.Not.Null);
260+
Assert.That(Regex.Matches(sqlLog.Appender.GetEvents()[0].RenderedMessage, "PropRefEntity").Count, Is.EqualTo(1));
222261
}
223262
}
224263

@@ -234,28 +273,41 @@ public void NullableOneToOneFetchQueryIsNotAffected()
234273
"select ex "
235274
+ "from NullableOwner ex left join fetch ex.OneToOne o "
236275
+ "where o is null "
237-
).SetMaxResults(1)
276+
)
238277
.UniqueResult<NullableOwner>();
239278

279+
Assert.That(entity, Is.Not.Null);
240280
Assert.That(Regex.Matches(sqlLog.GetWholeLog(), "OneToOneEntity").Count, Is.EqualTo(1));
241281
}
242282
}
243-
283+
284+
[Test(Description = "GH-2611")]
285+
public void NullableOneIsNullLinq()
286+
{
287+
using (var session = OpenSession())
288+
{
289+
var entity = session.Query<NullableOwner>().Where(x => x.OneToOne == null).FirstOrDefault();
290+
Assert.That(entity, Is.Not.Null);
291+
}
292+
}
293+
244294
[Test]
245295
public void NullableOneToOneFetchQueryIsNotAffected2()
246296
{
247297
using (var sqlLog = new SqlLogSpy())
248298
using (var session = OpenSession())
249299
{
250-
var entity =
300+
var entities =
251301
session
252302
.CreateQuery(
253303
"select ex "
254304
+ "from NullableOwner ex left join fetch ex.OneToOne o "
255305
+ "where o.Id is null "
256-
).SetMaxResults(1)
257-
.UniqueResult<NullableOwner>();
306+
)
307+
.List<NullableOwner>();
308+
var entity = entities[0];
258309

310+
Assert.That(entity, Is.Not.Null);
259311
Assert.That(Regex.Matches(sqlLog.GetWholeLog(), "OneToOneEntity").Count, Is.EqualTo(1));
260312
}
261313
}
@@ -493,8 +545,9 @@ protected override HbmMapping GetMappings()
493545
mapper.Class<OneToOneEntity>(
494546
rc =>
495547
{
496-
rc.Id(e => e.Id, m => m.Generator(Generators.GuidComb));
548+
rc.Id(e => e.Id, m => m.Generator(Generators.Foreign<OneToOneEntity>(x => x.Parent)));
497549
rc.Property(e => e.Name);
550+
rc.OneToOne(x => x.Parent, x => x.Constrained(true));
498551
});
499552

500553
mapper.Class<PropRefEntity>(
@@ -510,7 +563,11 @@ protected override HbmMapping GetMappings()
510563
{
511564
rc.Id(e => e.Id, m => m.Generator(Generators.GuidComb));
512565
rc.Property(e => e.Name);
513-
rc.OneToOne(e => e.OneToOne, m => m.Constrained(false));
566+
rc.OneToOne(e => e.OneToOne, m =>
567+
{
568+
m.Constrained(false);
569+
m.Cascade(Mapping.ByCode.Cascade.All);
570+
});
514571
rc.ManyToOne(
515572
e => e.PropRef,
516573
m =>
@@ -519,6 +576,7 @@ protected override HbmMapping GetMappings()
519576
m.PropertyRef(nameof(PropRefEntity.PropertyRef));
520577
m.ForeignKey("none");
521578
m.NotFound(NotFoundMode.Ignore);
579+
m.Cascade(Mapping.ByCode.Cascade.All);
522580
});
523581
});
524582

@@ -589,7 +647,10 @@ protected override void OnSetUp()
589647
};
590648
session.Save(_noAssociation);
591649

592-
session.Flush();
650+
session.Save(new NullableOwner() {Name = "NoAssociation"});
651+
var nullableOwner = new NullableOwner() {Name = "Association", PropRef = new PropRefEntity() {Name = "x", PropertyRef = "xx"}, OneToOne = new OneToOneEntity() {Name = "x"},};
652+
nullableOwner.OneToOne.Parent = nullableOwner;
653+
session.Save(nullableOwner);
593654
transaction.Commit();
594655
}
595656
}

0 commit comments

Comments
 (0)