Skip to content

Commit 11bc49d

Browse files
bahusoidhazzik
authored andcommitted
Port HQL Entity Join
Fixes #981
1 parent db0f002 commit 11bc49d

14 files changed

+815
-16
lines changed
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
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 NHibernate.Cfg.MappingSchema;
12+
using NHibernate.Mapping.ByCode;
13+
using NHibernate.Test.Hql.EntityJoinHqlTestEntities;
14+
using NUnit.Framework;
15+
16+
namespace NHibernate.Test.Hql
17+
{
18+
using System.Threading.Tasks;
19+
/// <summary>
20+
/// Tests for explicit entity joins (not associated entities)
21+
/// </summary>
22+
[TestFixture]
23+
public class EntityJoinHqlTestAsync : TestCaseMappingByCode
24+
{
25+
private const string _customEntityName = "CustomEntityName";
26+
private EntityWithCompositeId _entityWithCompositeId;
27+
private EntityWithNoAssociation _noAssociation;
28+
private EntityCustomEntityName _entityWithCustomEntityName;
29+
30+
[Test]
31+
public async Task CanJoinNotAssociatedEntityAsync()
32+
{
33+
using (var sqlLog = new SqlLogSpy())
34+
using (var session = OpenSession())
35+
{
36+
EntityComplex entityComplex =
37+
await (session
38+
.CreateQuery("select ex " +
39+
"from EntityWithNoAssociation root " +
40+
"left join EntityComplex ex with root.Complex1Id = ex.Id")
41+
.SetMaxResults(1)
42+
.UniqueResultAsync<EntityComplex>());
43+
44+
Assert.That(entityComplex, Is.Not.Null);
45+
Assert.That(NHibernateUtil.IsInitialized(entityComplex), Is.True);
46+
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
47+
}
48+
}
49+
50+
[Test]
51+
public async Task EntityJoinForCompositeKeyAsync()
52+
{
53+
using (var sqlLog = new SqlLogSpy())
54+
using (var session = OpenSession())
55+
{
56+
await (session.CreateQuery(
57+
"select root, ejComposite from EntityWithNoAssociation root " +
58+
"inner join EntityWithCompositeId ejComposite " +
59+
" with (root.Composite1Key1 = ejComposite.Key.Id1 and root.Composite1Key2 = ejComposite.Key.Id2)")
60+
.SetMaxResults(1).ListAsync());
61+
62+
var composite = await (session.LoadAsync<EntityWithCompositeId>(_entityWithCompositeId.Key));
63+
64+
Assert.That(composite, Is.Not.Null);
65+
Assert.That(NHibernateUtil.IsInitialized(composite), Is.True, "Object must be initialized");
66+
Assert.That(composite, Is.EqualTo(_entityWithCompositeId).Using((EntityWithCompositeId x, EntityWithCompositeId y) => (Equals(x.Key, y.Key) && Equals(x.Name, y.Name)) ? 0 : 1));
67+
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
68+
}
69+
}
70+
71+
[Test]
72+
public async Task NullLeftEntityJoinAsync()
73+
{
74+
using (var sqlLog = new SqlLogSpy())
75+
using (var session = OpenSession())
76+
{
77+
var objs =await (session.CreateQuery(
78+
"select ejLeftNull, root " +
79+
"from EntityWithNoAssociation root " +
80+
"left join EntityComplex ejLeftNull with ejLeftNull.Id is null")
81+
.SetMaxResults(1).UniqueResultAsync<object[]>());
82+
EntityComplex ejLeftNull = (EntityComplex)objs[0];
83+
EntityWithNoAssociation root = (EntityWithNoAssociation) objs[1];
84+
85+
Assert.That(root, Is.Not.Null, "root should not be null (looks like left join didn't work)");
86+
Assert.That(NHibernateUtil.IsInitialized(root), Is.True);
87+
Assert.That(ejLeftNull, Is.Null);
88+
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
89+
}
90+
}
91+
92+
[Test]
93+
public async Task EntityJoinForCustomEntityNameAsync()
94+
{
95+
using (var sqlLog = new SqlLogSpy())
96+
using (var session = OpenSession())
97+
{
98+
EntityCustomEntityName ejCustomEntity = await (session.CreateQuery(
99+
$"select ejCustomEntity from EntityWithNoAssociation root inner join {_customEntityName} ejCustomEntity with ejCustomEntity.Id = root.CustomEntityNameId")
100+
.SetMaxResults(1).UniqueResultAsync<EntityCustomEntityName>());
101+
102+
Assert.That(ejCustomEntity, Is.Not.Null);
103+
Assert.That(NHibernateUtil.IsInitialized(ejCustomEntity), Is.True);
104+
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
105+
}
106+
}
107+
108+
[Test]
109+
public async Task EntityJoinFoSubqueryAsync()
110+
{
111+
using (var sqlLog = new SqlLogSpy())
112+
using (var session = OpenSession())
113+
{
114+
EntityComplex ej = null;
115+
EntityWithNoAssociation root = null;
116+
117+
var subquery = "from EntityWithNoAssociation rootSub " +
118+
"inner join EntityComplex ejSub with rootSub.Complex1Id = ejSub.Id " +
119+
"where ejSub.Name = ej.Name";
120+
var objs = await (session.CreateQuery(
121+
"select root, ej from EntityWithNoAssociation root " +
122+
"inner join EntityComplex ej with root.Complex1Id = ej.Id " +
123+
$"where exists ({subquery})")
124+
.UniqueResultAsync<object[]>());
125+
root = (EntityWithNoAssociation) objs[0];
126+
ej = (EntityComplex)objs[1];
127+
128+
Assert.That(NHibernateUtil.IsInitialized(root), Is.True);
129+
Assert.That(ej, Is.Not.Null);
130+
Assert.That(NHibernateUtil.IsInitialized(ej), Is.True);
131+
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
132+
}
133+
}
134+
135+
[Test, Ignore("Failing for unrelated reasons")]
136+
public async Task CrossJoinAndWithClauseAsync()
137+
{
138+
//This is about complex theta style join fix that was implemented in hibernate along with entity join functionality
139+
//https://hibernate.atlassian.net/browse/HHH-7321
140+
using (var sqlLog = new SqlLogSpy())
141+
using (var session = OpenSession())
142+
{
143+
await (session.CreateQuery(
144+
"SELECT s " +
145+
"FROM EntityComplex s, EntityComplex q " +
146+
"LEFT JOIN s.SameTypeChild AS sa WITH sa.SameTypeChild.Id = q.SameTypeChild.Id"
147+
).ListAsync());
148+
}
149+
}
150+
151+
#region Test Setup
152+
153+
protected override HbmMapping GetMappings()
154+
{
155+
var mapper = new ModelMapper();
156+
mapper.Class<EntityComplex>(
157+
rc =>
158+
{
159+
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
160+
161+
rc.Version(ep => ep.Version, vm => { });
162+
163+
rc.Property(x => x.Name);
164+
165+
rc.Property(ep => ep.LazyProp, m => m.Lazy(true));
166+
167+
rc.ManyToOne(ep => ep.SameTypeChild, m => m.Column("SameTypeChildId"));
168+
169+
170+
});
171+
172+
173+
mapper.Class<EntityWithCompositeId>(
174+
rc =>
175+
{
176+
rc.ComponentAsId(
177+
e => e.Key,
178+
ekm =>
179+
{
180+
ekm.Property(ek => ek.Id1);
181+
ekm.Property(ek => ek.Id2);
182+
});
183+
184+
rc.Property(e => e.Name);
185+
});
186+
187+
mapper.Class<EntityWithCompositeId>(
188+
rc =>
189+
{
190+
rc.ComponentAsId(
191+
e => e.Key,
192+
ekm =>
193+
{
194+
ekm.Property(ek => ek.Id1);
195+
ekm.Property(ek => ek.Id2);
196+
});
197+
198+
rc.Property(e => e.Name);
199+
});
200+
201+
mapper.Class<EntityWithNoAssociation>(
202+
rc =>
203+
{
204+
rc.Id(e => e.Id, m => m.Generator(Generators.GuidComb));
205+
206+
rc.Property(e => e.Complex1Id);
207+
rc.Property(e => e.Complex2Id);
208+
rc.Property(e => e.Simple1Id);
209+
rc.Property(e => e.Simple2Id);
210+
rc.Property(e => e.Composite1Key1);
211+
rc.Property(e => e.Composite1Key2);
212+
rc.Property(e => e.CustomEntityNameId);
213+
214+
});
215+
216+
mapper.Class<EntityCustomEntityName>(
217+
rc =>
218+
{
219+
rc.EntityName(_customEntityName);
220+
221+
rc.Id(e => e.Id, m => m.Generator(Generators.GuidComb));
222+
rc.Property(e => e.Name);
223+
});
224+
225+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
226+
}
227+
228+
protected override void OnTearDown()
229+
{
230+
using (ISession session = OpenSession())
231+
using (ITransaction transaction = session.BeginTransaction())
232+
{
233+
session.Delete("from System.Object");
234+
235+
session.Flush();
236+
transaction.Commit();
237+
}
238+
}
239+
240+
protected override void OnSetUp()
241+
{
242+
using (var session = OpenSession())
243+
using (var transaction = session.BeginTransaction())
244+
{
245+
var parent = new EntityComplex
246+
{
247+
Name = "ComplexEnityParent",
248+
LazyProp = "SomeBigValue",
249+
SameTypeChild = new EntityComplex()
250+
{
251+
Name = "ComplexEntityChild"
252+
}
253+
};
254+
255+
_entityWithCompositeId = new EntityWithCompositeId
256+
{
257+
Key = new CompositeKey
258+
{
259+
Id1 = 1,
260+
Id2 = 2
261+
},
262+
Name = "Composite"
263+
};
264+
265+
session.Save(parent.SameTypeChild);
266+
session.Save(parent);
267+
session.Save(_entityWithCompositeId);
268+
269+
_entityWithCustomEntityName = new EntityCustomEntityName()
270+
{
271+
Name = "EntityCustomEntityName"
272+
};
273+
274+
session.Save(_customEntityName, _entityWithCustomEntityName);
275+
276+
_noAssociation = new EntityWithNoAssociation()
277+
{
278+
Complex1Id = parent.Id,
279+
Complex2Id = parent.SameTypeChild.Id,
280+
Composite1Key1 = _entityWithCompositeId.Key.Id1,
281+
Composite1Key2 = _entityWithCompositeId.Key.Id2,
282+
CustomEntityNameId = _entityWithCustomEntityName.Id
283+
};
284+
285+
session.Save(_noAssociation);
286+
287+
session.Flush();
288+
transaction.Commit();
289+
}
290+
}
291+
292+
#endregion Test Setup
293+
}
294+
}

0 commit comments

Comments
 (0)