Skip to content

Commit 71aa211

Browse files
gmalyshevbahusoidfredericDelaporte
committed
NH-2016 - Fix joining to association twice using criteria (#287)
Fixes #990 Co-authored-by: Roman Artiukhin <bahusdrive@gmail.com> Co-authored-by: Frédéric Delaporte <12201973+fredericdelaporte@users.noreply.github.com>
1 parent 8649f9a commit 71aa211

File tree

7 files changed

+674
-82
lines changed

7 files changed

+674
-82
lines changed
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
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.Collections.Generic;
12+
using NHibernate.Criterion;
13+
using NHibernate.SqlCommand;
14+
using NUnit.Framework;
15+
16+
namespace NHibernate.Test.NHSpecificTest.NH3472
17+
{
18+
using System.Threading.Tasks;
19+
[TestFixture]
20+
public class FixtureAsync : BugTestCase
21+
{
22+
protected override void OnSetUp()
23+
{
24+
using (var s = OpenSession())
25+
using (var t = s.BeginTransaction())
26+
{
27+
var c = new Cat
28+
{
29+
Age = 6,
30+
Children = new HashSet<Cat>
31+
{
32+
new Cat
33+
{
34+
Age = 4,
35+
Children = new HashSet<Cat>
36+
{
37+
new Cat { Color = "Ginger", Age = 1 },
38+
new Cat { Color = "Black", Age = 3 }
39+
}
40+
}
41+
}
42+
};
43+
s.Save(c);
44+
t.Commit();
45+
}
46+
}
47+
48+
protected override void OnTearDown()
49+
{
50+
using (var s = OpenSession())
51+
using (var t = s.BeginTransaction())
52+
{
53+
s.Delete("from Cat");
54+
t.Commit();
55+
}
56+
}
57+
58+
[Test]
59+
public async Task CriteriaQueryWithMultipleJoinsToSameAssociationAsync()
60+
{
61+
using (var s = OpenSession())
62+
{
63+
var list =
64+
await (s
65+
.CreateCriteria<Cat>("cat")
66+
.CreateAlias(
67+
"cat.Children",
68+
"gingerCat",
69+
JoinType.LeftOuterJoin,
70+
Restrictions.Eq("Color", "Ginger"))
71+
.CreateAlias(
72+
"cat.Children",
73+
"blackCat",
74+
JoinType.LeftOuterJoin,
75+
Restrictions.Eq("Color", "Black"))
76+
.SetProjection(
77+
Projections.Alias(Projections.Property("gingerCat.Age"), "gingerCatAge"),
78+
Projections.Alias(Projections.Property("blackCat.Age"), "blackCatAge")
79+
).AddOrder(new Order(Projections.Property("Age"), true)).ListAsync<object[]>());
80+
Assert.That(list, Has.Count.EqualTo(4));
81+
Assert.That(list[0], Is.EqualTo(new object[] { null, null }));
82+
Assert.That(list[1], Is.EqualTo(new object[] { null, null }));
83+
Assert.That(list[2], Is.EqualTo(new object[] { 1, 3 }));
84+
Assert.That(list[3], Is.EqualTo(new object[] { null, null }));
85+
}
86+
}
87+
88+
[Test]
89+
public async Task QueryWithFetchesAndAliasDoNotDuplicateJoinAsync()
90+
{
91+
using (var s = OpenSession())
92+
{
93+
Cat parent = null;
94+
using (var spy = new SqlLogSpy())
95+
{
96+
var list =
97+
await (s
98+
.QueryOver<Cat>()
99+
.Fetch(SelectMode.Fetch, o => o.Parent)
100+
.Fetch(SelectMode.Fetch, o => o.Parent.Parent)
101+
.JoinAlias(o => o.Parent, () => parent)
102+
.Where(x => parent.Age == 4)
103+
.ListAsync());
104+
105+
// Two joins to Cat are expected: one for the immediate parent, and a second for the grand-parent.
106+
// So checking if it does not contain three joins or more. (The regex uses "[\s\S]" instead of "."
107+
// because the SQL is formatted by default and contains "\n" which are not matched by ".".)
108+
Assert.That(spy.GetWholeLog(), Does.Not.Match(@"(?:\bjoin\s*Cat\b[\s\S]*){3,}").IgnoreCase);
109+
Assert.That(list, Has.Count.EqualTo(2));
110+
Assert.That(
111+
NHibernateUtil.IsInitialized(list[0].Parent),
112+
Is.True,
113+
"first cat parent initialization status");
114+
Assert.That(
115+
NHibernateUtil.IsInitialized(list[1].Parent),
116+
Is.True,
117+
"second cat parent initialization status");
118+
Assert.That(
119+
NHibernateUtil.IsInitialized(list[0].Parent.Parent),
120+
Is.True,
121+
"first cat parent parent initialization status");
122+
Assert.That(
123+
NHibernateUtil.IsInitialized(list[1].Parent.Parent),
124+
Is.True,
125+
"second cat parent parent initialization status");
126+
}
127+
}
128+
}
129+
130+
[Test, Explicit("Debatable use case")]
131+
public async Task QueryWithFetchesAndMultipleJoinsToSameAssociationAsync()
132+
{
133+
using (var s = OpenSession())
134+
{
135+
Cat ginger = null;
136+
Cat black = null;
137+
var list =
138+
await (s
139+
.QueryOver<Cat>()
140+
.Fetch(SelectMode.Fetch, o => o.Children)
141+
.JoinAlias(o => o.Children, () => ginger)
142+
.Where(x => ginger.Color == "Ginger")
143+
.JoinAlias(o => o.Children, () => black)
144+
.Where(x => black.Color == "Black")
145+
.ListAsync());
146+
147+
Assert.That(list, Has.Count.EqualTo(1));
148+
Assert.That(list[0].Children, Has.Count.EqualTo(2));
149+
}
150+
}
151+
}
152+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System.Collections.Generic;
2+
3+
namespace NHibernate.Test.NHSpecificTest.NH3472
4+
{
5+
public class Cat
6+
{
7+
public virtual int Id { get; set; }
8+
9+
public virtual string Color { get; set; }
10+
public virtual int Age { get; set; }
11+
public virtual Cat Parent { get; set; }
12+
13+
public virtual ISet<Cat> Children { get; set; } = new HashSet<Cat>();
14+
}
15+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
using System.Collections.Generic;
2+
using NHibernate.Criterion;
3+
using NHibernate.SqlCommand;
4+
using NUnit.Framework;
5+
6+
namespace NHibernate.Test.NHSpecificTest.NH3472
7+
{
8+
[TestFixture]
9+
public class Fixture : BugTestCase
10+
{
11+
protected override void OnSetUp()
12+
{
13+
using (var s = OpenSession())
14+
using (var t = s.BeginTransaction())
15+
{
16+
var c = new Cat
17+
{
18+
Age = 6,
19+
Children = new HashSet<Cat>
20+
{
21+
new Cat
22+
{
23+
Age = 4,
24+
Children = new HashSet<Cat>
25+
{
26+
new Cat { Color = "Ginger", Age = 1 },
27+
new Cat { Color = "Black", Age = 3 }
28+
}
29+
}
30+
}
31+
};
32+
s.Save(c);
33+
t.Commit();
34+
}
35+
}
36+
37+
protected override void OnTearDown()
38+
{
39+
using (var s = OpenSession())
40+
using (var t = s.BeginTransaction())
41+
{
42+
s.Delete("from Cat");
43+
t.Commit();
44+
}
45+
}
46+
47+
[Test]
48+
public void CriteriaQueryWithMultipleJoinsToSameAssociation()
49+
{
50+
using (var s = OpenSession())
51+
{
52+
var list =
53+
s
54+
.CreateCriteria<Cat>("cat")
55+
.CreateAlias(
56+
"cat.Children",
57+
"gingerCat",
58+
JoinType.LeftOuterJoin,
59+
Restrictions.Eq("Color", "Ginger"))
60+
.CreateAlias(
61+
"cat.Children",
62+
"blackCat",
63+
JoinType.LeftOuterJoin,
64+
Restrictions.Eq("Color", "Black"))
65+
.SetProjection(
66+
Projections.Alias(Projections.Property("gingerCat.Age"), "gingerCatAge"),
67+
Projections.Alias(Projections.Property("blackCat.Age"), "blackCatAge")
68+
).AddOrder(new Order(Projections.Property("Age"), true)).List<object[]>();
69+
Assert.That(list, Has.Count.EqualTo(4));
70+
Assert.That(list[0], Is.EqualTo(new object[] { null, null }));
71+
Assert.That(list[1], Is.EqualTo(new object[] { null, null }));
72+
Assert.That(list[2], Is.EqualTo(new object[] { 1, 3 }));
73+
Assert.That(list[3], Is.EqualTo(new object[] { null, null }));
74+
}
75+
}
76+
77+
[Test]
78+
public void QueryWithFetchesAndAliasDoNotDuplicateJoin()
79+
{
80+
using (var s = OpenSession())
81+
{
82+
Cat parent = null;
83+
using (var spy = new SqlLogSpy())
84+
{
85+
var list =
86+
s
87+
.QueryOver<Cat>()
88+
.Fetch(SelectMode.Fetch, o => o.Parent)
89+
.Fetch(SelectMode.Fetch, o => o.Parent.Parent)
90+
.JoinAlias(o => o.Parent, () => parent)
91+
.Where(x => parent.Age == 4)
92+
.List();
93+
94+
// Two joins to Cat are expected: one for the immediate parent, and a second for the grand-parent.
95+
// So checking if it does not contain three joins or more. (The regex uses "[\s\S]" instead of "."
96+
// because the SQL is formatted by default and contains "\n" which are not matched by ".".)
97+
Assert.That(spy.GetWholeLog(), Does.Not.Match(@"(?:\bjoin\s*Cat\b[\s\S]*){3,}").IgnoreCase);
98+
Assert.That(list, Has.Count.EqualTo(2));
99+
Assert.That(
100+
NHibernateUtil.IsInitialized(list[0].Parent),
101+
Is.True,
102+
"first cat parent initialization status");
103+
Assert.That(
104+
NHibernateUtil.IsInitialized(list[1].Parent),
105+
Is.True,
106+
"second cat parent initialization status");
107+
Assert.That(
108+
NHibernateUtil.IsInitialized(list[0].Parent.Parent),
109+
Is.True,
110+
"first cat parent parent initialization status");
111+
Assert.That(
112+
NHibernateUtil.IsInitialized(list[1].Parent.Parent),
113+
Is.True,
114+
"second cat parent parent initialization status");
115+
}
116+
}
117+
}
118+
119+
[Test, Explicit("Debatable use case")]
120+
public void QueryWithFetchesAndMultipleJoinsToSameAssociation()
121+
{
122+
using (var s = OpenSession())
123+
{
124+
Cat ginger = null;
125+
Cat black = null;
126+
var list =
127+
s
128+
.QueryOver<Cat>()
129+
.Fetch(SelectMode.Fetch, o => o.Children)
130+
.JoinAlias(o => o.Children, () => ginger)
131+
.Where(x => ginger.Color == "Ginger")
132+
.JoinAlias(o => o.Children, () => black)
133+
.Where(x => black.Color == "Black")
134+
.List();
135+
136+
Assert.That(list, Has.Count.EqualTo(1));
137+
Assert.That(list[0].Children, Has.Count.EqualTo(2));
138+
}
139+
}
140+
}
141+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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.NH3472">
5+
6+
<class name="Cat">
7+
<id name="Id" generator="native"/>
8+
9+
<property name="Color"/>
10+
<property name="Age"/>
11+
<many-to-one name="Parent" column="parentId" insert="false" update="false"/>
12+
13+
<set name="Children" cascade="all">
14+
<key column="parentId"/>
15+
<one-to-many class="Cat"/>
16+
</set>
17+
</class>
18+
19+
</hibernate-mapping>

0 commit comments

Comments
 (0)