我有一个名为Member的实体。会员可以跟随许多其他会员(根据域名),因此涉及多对多关系。我在我的数据库中创建了一个关系表(member_follows)。使用Fluent NHibernate我还专门用一个新的实体“MemberFollow”来映射这种关系,如下所示:
public class MemberMap : MapBase<Member>
{
public MemberMap()
: base()
{
Table("members");
Map(x => x.Id ).Column("id" );
Map(x => x.Fullname).Column("fullname");
}
public class MemberFollowMap : MapBase<MemberFollow>
{
public MemberFollowMap()
: base()
{
Table("members_follows");
Map(x => x.Id).Column("id");
References<Member>(x => x.Follower)
.Column("follower_id")
.Fetch.Join();
References<Member>(x => x.Member)
.Column("member_id");
.Fetch.Join();
}
}
由于MemberFollow映射的FetchMode设置为Join,我希望此查询在一个查询中获取成员。但是当我查看日志时,我看到NHibernate执行一个简单的选择来查找每个跟随成员的ID,并在访问时逐个加载成员。
public IList<Member> ListFollowings(Int32 FollwerId, Int32 Start, Int32 Size, String SortBy, SortOrder OrderBy)
{
DetachedCriteria Filter = DetachedCriteria.For<MemberFollow>();
Filter.Add (Expression.Eq("Follower.Id", FollwerId));
Filter.AddOrder (OrderBy == SortOrder.Asc ? Order.Asc(SortBy) : Order.Desc(SortBy));
Filter.SetProjection (Projections.Property("Member"));
Filter.SetFirstResult(Start);
Filter.SetMaxResults (Size );
return Find<Member>(Filter);
}
所以我的问题是:为什么NHibernate会忽略映射类设置的FetchMode? p>
答案 0 :(得分:0)
我想你可能从错误的角度来看待它。在NHibernate中,将多对多关系显式映射为模型对象是很不寻常的。请参阅下面的更改提案。
给定域对象MyMember
及其覆盖映射:
public class MyMember : DomainObjectBase
{
public virtual string Name { get; set; }
public virtual IList<MyMember> Follows { get; set; }
}
public class MemberOverride : IAutoMappingOverride<MyMember>
{
public void Override(AutoMapping mapping)
{
mapping.HasManyToMany<MyMember> (x => x.Follows)
.Table("FollowMap")
.ParentKeyColumn("FollowerID")
.ChildKeyColumn("FollowedID")
.Cascade.SaveUpdate(); ;
}
}
以下测试通过:
[Test]
public void WhoFollowsWho()
{
var a = new MyMember {Name = "A"};
var b = new MyMember {Name = "B"};
var c = new MyMember {Name = "C"};
var d = new MyMember {Name = "D"};
var e = new MyMember {Name = "E"};
a.Follows = new List<MyMember> { b, c, d, e };
d.Follows = new List<MyMember> { a, c, e };
using (var t = Session.BeginTransaction())
{
Session.Save(a);
Session.Save(b);
Session.Save(c);
Session.Save(d);
Session.Save(e);
t.Commit();
}
using (var t = Session.BeginTransaction())
{
DetachedCriteria followersOfC = DetachedCriteria.For<MyMember>();
followersOfC.CreateCriteria("Follows")
.Add(Expression.Eq("Id", c.Id))
.SetProjection(Projections.Property("Name"));
var results = followersOfC.GetExecutableCriteria(Session).List();
t.Commit();
CollectionAssert.AreEquivalent(new[]{"A", "D"}, results);
}
using (var t = Session.BeginTransaction())
{
DetachedCriteria followedByA = DetachedCriteria.For<MyMember>();
followedByA.CreateAlias("Follows", "f")
.Add(Expression.Eq("Id", a.Id))
.SetProjection(Projections.Property("f.Name"));
var results = followedByA.GetExecutableCriteria(Session).List();
t.Commit();
CollectionAssert.AreEquivalent(new[]{"B", "C", "D", "E"}, results);
}
}
正如预期的那样,生成的SQL依赖于内连接:
NHibernate:
SELECT this_.Name as y0_ FROM "MyMember" this_
inner join FollowMap follows3_ on this_.Id=follows3_.FollowerID
inner join "MyMember" mymember1_ on follows3_.FollowedID=mymember1_.Id
WHERE mymember1_.Id = @p0
NHibernate:
SELECT f1_.Name as y0_ FROM "MyMember" this_
inner join FollowMap follows3_ on this_.Id=follows3_.FollowerID
inner join "MyMember" f1_ on follows3_.FollowedID=f1_.Id
WHERE this_.Id = @p0
注意:如果您检索MyMember的完整实例,而不是仅检索每个MyMember的“Name”属性,则SQL语句将保持相同的形状。只有其他投影才会添加到SELECT子句中。但是,您必须修复测试以使其再次通过; - )
注2:如果您愿意处理包含自己属性的多对多关系,则来自post的这个Kyle Baley和此{来自Nhibernate博客的{3}}可能会就此主题提供一些帮助。
注3:我试了一下: - )
鉴于域对象MySecondMember
和MyFollowMap
及其覆盖映射:
public class MySecondMember : DomainObjectBase
{
public virtual string Name { get; set; }
public virtual IList<MyFollowMap> Follows { get; set; }
}
public class MyFollowMap : DomainObjectBase
{
public virtual MySecondMember Who { get; set; }
public virtual DateTime StartedToFollowOn { get; set; }
}
public class MemberSecondOverride : IAutoMappingOverride<MySecondMember>
{
public void Override(AutoMapping mapping)
{
mapping.HasMany(x => x.Follows);
}
}
以下测试通过:
[Test]
public void WhoFollowsWho2()
{
var a = new MySecondMember { Name = "A" };
var b = new MySecondMember { Name = "B" };
var c = new MySecondMember { Name = "C" };
var d = new MySecondMember { Name = "D" };
var e = new MySecondMember { Name = "E" };
var bfm = new MyFollowMap { Who = b, StartedToFollowOn = DateTime.UtcNow };
var cfm = new MyFollowMap { Who = c, StartedToFollowOn = DateTime.UtcNow };
var dfm = new MyFollowMap { Who = d, StartedToFollowOn = DateTime.UtcNow };
var efm = new MyFollowMap { Who = e, StartedToFollowOn = DateTime.UtcNow };
a.Follows = new List { bfm, cfm, dfm, efm };
var afm = new MyFollowMap { Who = a, StartedToFollowOn = DateTime.UtcNow };
cfm = new MyFollowMap { Who = c, StartedToFollowOn = DateTime.UtcNow };
efm = new MyFollowMap { Who = e, StartedToFollowOn = DateTime.UtcNow };
d.Follows = new List { afm, cfm, efm };
using (var t = Session.BeginTransaction())
{
Session.Save(a);
Session.Save(b);
Session.Save(c);
Session.Save(d);
Session.Save(e);
t.Commit();
}
using (var t = Session.BeginTransaction())
{
DetachedCriteria followersOfC = DetachedCriteria.For<MySecondMember>();
followersOfC.CreateAlias("Follows", "f")
.CreateAlias("f.Who", "w")
.Add(Expression.Eq("w.Id", c.Id))
.SetProjection(Projections.Property("Name"));
var results = followersOfC.GetExecutableCriteria(Session).List();
t.Commit();
CollectionAssert.AreEquivalent(new[] { "A", "D" }, results);
}
using (var t = Session.BeginTransaction())
{
DetachedCriteria followedByA = DetachedCriteria.For<MySecondMember>();
followedByA
.CreateAlias("Follows", "f")
.CreateAlias("f.Who", "w")
.Add(Expression.Eq("Id", a.Id))
.SetProjection(Projections.Property("w.Name"));
var results = followedByA.GetExecutableCriteria(Session).List();
t.Commit();
CollectionAssert.AreEquivalent(new[] { "B", "C", "D", "E" }, results);
}
}
正如预期的那样,生成的SQL依赖于内连接:
NHibernate:
SELECT this_.Name as y0_ FROM "MySecondMember" this_
inner join "MyFollowMap" f1_ on this_.Id=f1_.MySecondMember_id
inner join "MySecondMember" w2_ on f1_.Who_id=w2_.Id
WHERE w2_.Id = @p0;
NHibernate:
SELECT w2_.Name as y0_ FROM "MySecondMember" this_
inner join "MyFollowMap" f1_ on this_.Id=f1_.MySecondMember_id
inner join "MySecondMember" w2_ on f1_.Who_id=w2_.Id
WHERE this_.Id = @p0