NHibernate が内部結合を行う場合、子テーブルから ID を取得せず、親テーブルから ID を取得しません (ただし、それらは同じです)。
例:
TableParent (ID, Name)
TableChild (ID, ID_TableParent, ....)
nHibernate が内部結合を行う場合、次のようになります。
select c.ID, c.ID_TableParent, p.Name
from TableChild c
inner join TableParent p on p.ID = c.ID_TableParent
nHibernate が左外部結合を行う場合、次のようになります。
select c.ID, c.ID_TableParent, p.ID, p.Name
from TableChild c
left outer join TableParent p on p.ID = c.ID_TableParent
また、NHibernate の内部動作により、2 番目のクエリから 2 つのエンティティを作成できます。TableChild 用の 1 つのエンティティと TableParent 用の 1 つのエンティティ。
最初のクエリでは、TableChild エンティティのみを取得し、場合によっては p.Name が無視され (おそらく第 2 レベルで)、TableParent を参照するプロパティをチェックする際にデータベースを再クエリします。
データベースへのヒットが 1 つだけのツリー構造をロードしたいときに、これを見つけました。
public class SysPermissionTree
{
public virtual int ID { get; set; }
public virtual SysPermissionTree Parent { get; set; }
public virtual string Name_L1 { get; set; }
public virtual string Name_L2 { get; set; }
public virtual Iesi.Collections.Generic.ISet<SysPermissionTree> Children { get; private set; }
public virtual Iesi.Collections.Generic.ISet<SysPermission> Permissions { get; private set; }
public class SysPermissionTree_Map : ClassMap<SysPermissionTree>
{
public SysPermissionTree_Map()
{
Id(x => x.ID).GeneratedBy.Identity();
References(x => x.Parent, "id_SysPermissionTree_Parent");
Map(x => x.Name_L1);
Map(x => x.Name_L2);
HasMany(x => x.Children).KeyColumn("id_SysPermissionTree_Parent").AsSet();
HasMany(x => x.Permissions).KeyColumn("id_SysPermissionTree").AsSet();
}
}
}
そして、私が使用したクエリは次のとおりです。
SysPermissionTree t = null;
SysPermission p = null;
return db.QueryOver<SysPermissionTree>()
.JoinAlias(x => x.Children, () => t, NHibernate.SqlCommand.JoinType.LeftOuterJoin)
.JoinAlias(() => t.Permissions, () => p, NHibernate.SqlCommand.JoinType.LeftOuterJoin)
.Where(x => x.Parent == null)
.TransformUsing(Transformers.DistinctRootEntity)
.List();
NHibernate.SqlCommand.JoinType.LeftOuterJoin を使用。InnerJoin を使用した場合、構造は 1 つのクエリだけでは読み込まれませんでした。NHibernate がエンティティを認識できるように、LeftOuterJoin を使用する必要がありました。
実行された SQL クエリは次のとおりです。
SELECT this_.ID as ID28_2_, this_.Name_L1 as Name2_28_2_, this_.Name_L2 as Name3_28_2_, this_.id_SysPermissionTree_Parent as id4_28_2_, t1_.id_SysPermissionTree_Parent as id4_4_, t1_.ID as ID4_, t1_.ID as ID28_0_, t1_.Name_L1 as Name2_28_0_, t1_.Name_L2 as Name3_28_0_, t1_.id_SysPermissionTree_Parent as id4_28_0_, p2_.id_SysPermissionTree as id4_5_, p2_.ID as ID5_, p2_.ID as ID27_1_, p2_.Name_L1 as Name2_27_1_, p2_.Name_L2 as Name3_27_1_, p2_.id_SysPermissionTree as id4_27_1_ FROM [SysPermissionTree] this_ left outer join [SysPermissionTree] t1_ on this_.ID=t1_.id_SysPermissionTree_Parent left outer join [SysPermission] p2_ on t1_.ID=p2_.id_SysPermissionTree WHERE this_.id_SysPermissionTree_Parent is null
SELECT this_.ID as ID28_2_, this_.Name_L1 as Name2_28_2_, this_.Name_L2 as Name3_28_2_, this_.id_SysPermissionTree_Parent as id4_28_2_, t1_.ID as ID28_0_, t1_.Name_L1 as Name2_28_0_, t1_.Name_L2 as Name3_28_0_, t1_.id_SysPermissionTree_Parent as id4_28_0_, p2_.ID as ID27_1_, p2_.Name_L1 as Name2_27_1_, p2_.Name_L2 as Name3_27_1_, p2_.id_SysPermissionTree as id4_27_1_ FROM [SysPermissionTree] this_ inner join [SysPermissionTree] t1_ on this_.ID=t1_.id_SysPermissionTree_Parent inner join [SysPermission] p2_ on t1_.ID=p2_.id_SysPermissionTree WHERE this_.id_SysPermissionTree_Parent is null
ここで、最初のクエリは左外部結合であり、2 つの追加フィールドを取得します: t1_.id_SysPermissionTree_Parent は id4_4_ として、t1_.ID は ID4_ として
つまり、NHibernate を使用する場合、NHibernate の内部動作に準拠するために左外部結合が必要になる場合があるということです。