Skip to content

Commit 116d34a

Browse files
authored
Fix fetching many-to-many with subclasses (nhibernate#3252)
Fixes nhibernate#3239
1 parent fe7b6f0 commit 116d34a

File tree

5 files changed

+80
-7
lines changed

5 files changed

+80
-7
lines changed

src/NHibernate.Test/Async/NHSpecificTest/NH2174/Fixture.cs

+27-1
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ protected override void OnSetUp()
2525
{
2626
var doc = new Document {Id_Base = 1, Id_Doc = 2};
2727
session.Save(doc);
28-
session.Save(new DocumentDetailDocument {Id_Base = 1, Id_Doc = 2, Id_Item = 1, ReferencedDocument = doc});
28+
var detail = new DocumentDetailDocument {Id_Base = 1, Id_Doc = 2, Id_Item = 1, ReferencedDocument = doc};
29+
session.Save(detail);
2930

31+
doc.RefferedDetailsManyToMany.Add(detail);
3032
transaction.Commit();
3133
}
3234
}
@@ -53,6 +55,14 @@ public async Task LinqFetchAsync()
5355
}
5456
}
5557

58+
[Test(Description = "GH-3239")]
59+
public async Task LinqFetchManyToManyAsync()
60+
{
61+
using var session = OpenSession();
62+
var result = await (session.Query<Document>().Fetch(x => x.RefferedDetailsManyToMany).FirstAsync());
63+
Assert.That(result.RefferedDetailsManyToMany, Has.Count.EqualTo(1));
64+
}
65+
5666
[Test]
5767
public async Task QueryOverFetchAsync()
5868
{
@@ -63,6 +73,14 @@ public async Task QueryOverFetchAsync()
6373
}
6474
}
6575

76+
[Test(Description = "GH-3239")]
77+
public async Task QueryOverFetchManyToManyAsync()
78+
{
79+
using var session = OpenSession();
80+
var result = await (session.QueryOver<Document>().Fetch(SelectMode.Fetch, x => x.RefferedDetailsManyToMany).SingleOrDefaultAsync());
81+
Assert.That(result.RefferedDetailsManyToMany, Has.Count.EqualTo(1));
82+
}
83+
6684
[Test]
6785
public async Task LazyLoadAsync()
6886
{
@@ -73,5 +91,13 @@ public async Task LazyLoadAsync()
7391
Assert.That(result.RefferedDetails.Count, Is.EqualTo(1));
7492
}
7593
}
94+
95+
[Test]
96+
public async Task LazyLoadManyToManyAsync()
97+
{
98+
using var session = OpenSession();
99+
var result = await (session.Query<Document>().FirstAsync());
100+
Assert.That(result.RefferedDetailsManyToMany.Count, Is.EqualTo(1));
101+
}
76102
}
77103
}

src/NHibernate.Test/NHSpecificTest/NH2174/Entity.cs

+1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public override int GetHashCode()
8686
private int _id_Doc;
8787
private int _id_base;
8888
public virtual IList<DocumentDetailDocument> RefferedDetails { get; set; } = new List<DocumentDetailDocument>();
89+
public virtual IList<DocumentDetailDocument> RefferedDetailsManyToMany { get; set; } = new List<DocumentDetailDocument>();
8990

9091
public int Id_Doc
9192
{

src/NHibernate.Test/NHSpecificTest/NH2174/Fixture.cs

+27-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ protected override void OnSetUp()
1414
{
1515
var doc = new Document {Id_Base = 1, Id_Doc = 2};
1616
session.Save(doc);
17-
session.Save(new DocumentDetailDocument {Id_Base = 1, Id_Doc = 2, Id_Item = 1, ReferencedDocument = doc});
17+
var detail = new DocumentDetailDocument {Id_Base = 1, Id_Doc = 2, Id_Item = 1, ReferencedDocument = doc};
18+
session.Save(detail);
1819

20+
doc.RefferedDetailsManyToMany.Add(detail);
1921
transaction.Commit();
2022
}
2123
}
@@ -42,6 +44,14 @@ public void LinqFetch()
4244
}
4345
}
4446

47+
[Test(Description = "GH-3239")]
48+
public void LinqFetchManyToMany()
49+
{
50+
using var session = OpenSession();
51+
var result = session.Query<Document>().Fetch(x => x.RefferedDetailsManyToMany).First();
52+
Assert.That(result.RefferedDetailsManyToMany, Has.Count.EqualTo(1));
53+
}
54+
4555
[Test]
4656
public void QueryOverFetch()
4757
{
@@ -52,6 +62,14 @@ public void QueryOverFetch()
5262
}
5363
}
5464

65+
[Test(Description = "GH-3239")]
66+
public void QueryOverFetchManyToMany()
67+
{
68+
using var session = OpenSession();
69+
var result = session.QueryOver<Document>().Fetch(SelectMode.Fetch, x => x.RefferedDetailsManyToMany).SingleOrDefault();
70+
Assert.That(result.RefferedDetailsManyToMany, Has.Count.EqualTo(1));
71+
}
72+
5573
[Test]
5674
public void LazyLoad()
5775
{
@@ -62,5 +80,13 @@ public void LazyLoad()
6280
Assert.That(result.RefferedDetails.Count, Is.EqualTo(1));
6381
}
6482
}
83+
84+
[Test]
85+
public void LazyLoadManyToMany()
86+
{
87+
using var session = OpenSession();
88+
var result = session.Query<Document>().First();
89+
Assert.That(result.RefferedDetailsManyToMany.Count, Is.EqualTo(1));
90+
}
6591
}
6692
}

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

+13
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,18 @@
4242
</key>
4343
<one-to-many class="DocumentDetailDocument" />
4444
</bag>
45+
46+
<bag name="RefferedDetailsManyToMany" lazy="true" generic="true" cascade="all">
47+
<key>
48+
<column name="id_Doc_detail" />
49+
<column name="id_base_detail" />
50+
</key>
51+
<many-to-many class="DocumentDetailDocument" >
52+
<column name="id_item" />
53+
<column name="Version" />
54+
<column name="id_Doc" />
55+
<column name="id_base" />
56+
</many-to-many>
57+
</bag>
4558
</class>
4659
</hibernate-mapping>

src/NHibernate/Engine/TableGroupJoinHelper.cs

+12-5
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,18 @@ private static bool NeedsTableGroupJoin(IReadOnlyList<IJoin> joins, SqlString[]
6868

6969
foreach (var join in joins)
7070
{
71-
var entityPersister = GetEntityPersister(join.Joinable);
71+
var entityPersister = GetEntityPersister(join.Joinable, out var isManyToMany);
7272
if (entityPersister?.HasSubclassJoins(includeSubclasses && isSubclassIncluded(join.Alias)) != true)
7373
continue;
7474

7575
if (hasWithClause)
7676
return true;
7777

78-
if (entityPersister.ColumnsDependOnSubclassJoins(join.RHSColumns))
78+
if (!isManyToMany // many-to-many keys are stored in separate table
79+
&& entityPersister.ColumnsDependOnSubclassJoins(join.RHSColumns))
80+
{
7981
return true;
82+
}
8083
}
8184

8285
return false;
@@ -91,14 +94,16 @@ private static SqlString GetTableGroupJoinWithClause(SqlString[] withClauseFragm
9194
var isAssociationJoin = lhsColumns.Length > 0;
9295
if (isAssociationJoin)
9396
{
94-
var entityPersister = GetEntityPersister(first.Joinable);
97+
var entityPersister = GetEntityPersister(first.Joinable, out var isManyToMany);
9598
string rhsAlias = first.Alias;
9699
string[] rhsColumns = first.RHSColumns;
97100
for (int j = 0; j < lhsColumns.Length; j++)
98101
{
99102
fromFragment.Add(lhsColumns[j])
100103
.Add("=")
101-
.Add(entityPersister?.GenerateTableAliasForColumn(rhsAlias, rhsColumns[j]) ?? rhsAlias)
104+
.Add((entityPersister == null || isManyToMany) // many-to-many keys are stored in separate table
105+
? rhsAlias
106+
: entityPersister.GenerateTableAliasForColumn(rhsAlias, rhsColumns[j]))
102107
.Add(".")
103108
.Add(rhsColumns[j]);
104109
if (j != lhsColumns.Length - 1)
@@ -111,12 +116,14 @@ private static SqlString GetTableGroupJoinWithClause(SqlString[] withClauseFragm
111116
return fromFragment.ToSqlString();
112117
}
113118

114-
private static AbstractEntityPersister GetEntityPersister(IJoinable joinable)
119+
private static AbstractEntityPersister GetEntityPersister(IJoinable joinable, out bool isManyToMany)
115120
{
121+
isManyToMany = false;
116122
if (!joinable.IsCollection)
117123
return joinable as AbstractEntityPersister;
118124

119125
var collection = (IQueryableCollection) joinable;
126+
isManyToMany = collection.IsManyToMany;
120127
return collection.ElementType.IsEntityType ? collection.ElementPersister as AbstractEntityPersister : null;
121128
}
122129

0 commit comments

Comments
 (0)