diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH3134/ManyToManyChildFetchByCodeFixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH3134/ManyToManyChildFetchByCodeFixture.cs
new file mode 100644
index 00000000000..0a5b10467f9
--- /dev/null
+++ b/src/NHibernate.Test/Async/NHSpecificTest/GH3134/ManyToManyChildFetchByCodeFixture.cs
@@ -0,0 +1,90 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System.Collections.Generic;
+using System.Linq;
+using NHibernate.Cfg.MappingSchema;
+using NHibernate.Mapping.ByCode;
+using NHibernate.Transform;
+using NHibernate.Util;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.GH3134
+{
+ using System.Threading.Tasks;
+ [TestFixture]
+ public class ManyToManyChildFetchByCodeFixtureAsync : TestCaseMappingByCode
+ {
+ protected override HbmMapping GetMappings()
+ {
+ var mapper = new ModelMapper();
+ mapper.AddMapping();
+ mapper.AddMapping();
+
+ return mapper.CompileMappingForAllExplicitlyAddedEntities();
+ }
+ protected override void OnSetUp()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ var a1 = new A();
+ a1.Bs.Add(new B());
+ a1.Bs.Add(new B());
+ session.Save(a1);
+
+ var a2 = new A();
+ a2.Bs.Add(new B());
+ a2.Bs.Add(new B());
+ session.Save(a2);
+
+ transaction.Commit();
+ }
+ }
+ protected override void OnTearDown()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.Delete("from System.Object");
+
+ transaction.Commit();
+ }
+ }
+
+ [Test]
+ public async Task ChildFetchQueryOverAsync()
+ {
+ using (var session = OpenSession())
+ {
+ session.QueryOver().Future();
+
+ session.QueryOver()
+ .Fetch(SelectMode.ChildFetch, b => b)
+ .Fetch(SelectMode.Fetch, b => b.As)
+ .Future();
+
+ var result = (await (session.QueryOver()
+ .Fetch(SelectMode.ChildFetch, b => b, b => b.As)
+ .Fetch(SelectMode.Fetch, b => b.As.First().Bs)
+ .TransformUsing(Transformers.DistinctRootEntity)
+ .Future()
+ .GetEnumerableAsync()))
+ .ToList();
+
+ Assert.That(result.Count, Is.EqualTo(4));
+ Assert.That(NHibernateUtil.IsInitialized(result[0].As), Is.True);
+ Assert.That(NHibernateUtil.IsInitialized(result[0].As.First().Bs), Is.True);
+ Assert.That(result[0].As.Count, Is.EqualTo(1));
+ Assert.That(result[0].As.First().Bs.Count, Is.EqualTo(2));
+ }
+ }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH3134/A.cs b/src/NHibernate.Test/NHSpecificTest/GH3134/A.cs
new file mode 100644
index 00000000000..fab1a02c8ff
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH3134/A.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+
+namespace NHibernate.Test.NHSpecificTest.GH3134
+{
+ public class A
+ {
+ public virtual Guid Id { get; set; }
+ public virtual string NameA { get; set; }
+ public virtual ISet Bs { get; set; } = new HashSet();
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH3134/AMap.cs b/src/NHibernate.Test/NHSpecificTest/GH3134/AMap.cs
new file mode 100644
index 00000000000..5e02c36a746
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH3134/AMap.cs
@@ -0,0 +1,24 @@
+using NHibernate.Mapping.ByCode;
+using NHibernate.Mapping.ByCode.Conformist;
+
+namespace NHibernate.Test.NHSpecificTest.GH3134
+{
+ public class AMap : ClassMapping
+ {
+ public AMap()
+ {
+ this.Table("A");
+ this.Id(x => x.Id, mapper => { mapper.Column("IDA"); mapper.Generator(Generators.Guid); });
+ Property(x => x.NameA);
+ this.Set(
+ x => x.Bs,
+ collectionMapping =>
+ {
+ collectionMapping.Table("AB");
+ collectionMapping.Key(k => { k.Column("A"); k.ForeignKey("none");});
+ collectionMapping.Cascade(Mapping.ByCode.Cascade.All);
+ },
+ map => map.ManyToMany(m => { m.Column("B"); }));
+ }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH3134/B.cs b/src/NHibernate.Test/NHSpecificTest/GH3134/B.cs
new file mode 100644
index 00000000000..c06b30226e8
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH3134/B.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+
+namespace NHibernate.Test.NHSpecificTest.GH3134
+{
+ public class B
+ {
+ public virtual Guid Id { get; set; }
+ public virtual string NameB { get; set; }
+ public virtual ISet As { get; set; } = new HashSet();
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH3134/BMap.cs b/src/NHibernate.Test/NHSpecificTest/GH3134/BMap.cs
new file mode 100644
index 00000000000..998d4ffbbb5
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH3134/BMap.cs
@@ -0,0 +1,24 @@
+using NHibernate.Mapping.ByCode;
+using NHibernate.Mapping.ByCode.Conformist;
+
+namespace NHibernate.Test.NHSpecificTest.GH3134
+{
+ public class BMap : ClassMapping
+ {
+ public BMap()
+ {
+ this.Table("B");
+ this.Id(x => x.Id, mapper => { mapper.Column("IDB"); mapper.Generator(Generators.Guid); });
+ Property(x => x.NameB);
+ this.Set(
+ x => x.As,
+ collectionMapping =>
+ {
+ collectionMapping.Table("AB");
+ collectionMapping.Key(k => { k.Column("B"); k.ForeignKey("none"); });
+ collectionMapping.Cascade(Mapping.ByCode.Cascade.All);
+ },
+ map => map.ManyToMany(m => { m.Column("A"); }));
+ }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH3134/ManyToManyChildFetchByCodeFixture.cs b/src/NHibernate.Test/NHSpecificTest/GH3134/ManyToManyChildFetchByCodeFixture.cs
new file mode 100644
index 00000000000..78f359da012
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH3134/ManyToManyChildFetchByCodeFixture.cs
@@ -0,0 +1,79 @@
+using System.Collections.Generic;
+using System.Linq;
+using NHibernate.Cfg.MappingSchema;
+using NHibernate.Mapping.ByCode;
+using NHibernate.Transform;
+using NHibernate.Util;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.GH3134
+{
+ [TestFixture]
+ public class ManyToManyChildFetchByCodeFixture : TestCaseMappingByCode
+ {
+ protected override HbmMapping GetMappings()
+ {
+ var mapper = new ModelMapper();
+ mapper.AddMapping();
+ mapper.AddMapping();
+
+ return mapper.CompileMappingForAllExplicitlyAddedEntities();
+ }
+ protected override void OnSetUp()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ var a1 = new A();
+ a1.Bs.Add(new B());
+ a1.Bs.Add(new B());
+ session.Save(a1);
+
+ var a2 = new A();
+ a2.Bs.Add(new B());
+ a2.Bs.Add(new B());
+ session.Save(a2);
+
+ transaction.Commit();
+ }
+ }
+ protected override void OnTearDown()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.Delete("from System.Object");
+
+ transaction.Commit();
+ }
+ }
+
+ [Test]
+ public void ChildFetchQueryOver()
+ {
+ using (var session = OpenSession())
+ {
+ session.QueryOver().Future();
+
+ session.QueryOver()
+ .Fetch(SelectMode.ChildFetch, b => b)
+ .Fetch(SelectMode.Fetch, b => b.As)
+ .Future();
+
+ var result = session.QueryOver()
+ .Fetch(SelectMode.ChildFetch, b => b, b => b.As)
+ .Fetch(SelectMode.Fetch, b => b.As.First().Bs)
+ .TransformUsing(Transformers.DistinctRootEntity)
+ .Future()
+ .GetEnumerable()
+ .ToList();
+
+ Assert.That(result.Count, Is.EqualTo(4));
+ Assert.That(NHibernateUtil.IsInitialized(result[0].As), Is.True);
+ Assert.That(NHibernateUtil.IsInitialized(result[0].As.First().Bs), Is.True);
+ Assert.That(result[0].As.Count, Is.EqualTo(1));
+ Assert.That(result[0].As.First().Bs.Count, Is.EqualTo(2));
+ }
+ }
+ }
+}
diff --git a/src/NHibernate/Loader/OuterJoinableAssociation.cs b/src/NHibernate/Loader/OuterJoinableAssociation.cs
index 4f50cbfaf3c..c15615bcfe6 100644
--- a/src/NHibernate/Loader/OuterJoinableAssociation.cs
+++ b/src/NHibernate/Loader/OuterJoinableAssociation.cs
@@ -242,6 +242,9 @@ internal string GetSelectFragment(string entitySuffix, string collectionSuffix,
IncludeLazyProps = SelectMode == SelectMode.FetchLazyProperties,
});
case SelectMode.ChildFetch:
+ // Skip ChildFetch for many-to-many as element id is added by element persister.
+ if (Joinable.IsCollection && ((IQueryableCollection) Joinable).IsManyToMany)
+ return string.Empty;
return ReflectHelper.CastOrThrow(Joinable, "child fetch select mode")
.IdentifierSelectFragment(RHSAlias, entitySuffix);