2011-02-01 134 views
0

我有一个名为Member的实体。一位成员可以关注其他许多成员(根据域名),因此涉及多对多的关系。我在我的数据库中创建了关系表(member_follows)。使用功能NHibernate我也奉献一个新的实体“MemberFollow”映射这种关系如下图所示:为什么NHibernate忽略FetchMode.Join?

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设置为加入,我期待此查询到成员取一个查询。然而,当我查看日志时,我发现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?

回答

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的“Name”属性,而是检索MyMember的完整实例,则SQL语句保持相同的形状。只有附加投影被添加到SELECT子句中。但是,您必须修复测试以使其再次通过;-)

注意2:假设您愿意处理拥有属性的多对多关系,来自Nhibernate博客的从Kyle Baleyone可能会提供一些关于此主题的帮助。

注3:我给它一个尝试:-)

鉴于域对象MySecondMemberMyFollowMap和他们的被覆盖的映射:

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 
+0

Em,你说的很对,多对多关系的显式映射很少以这种方式完成,但背后的智慧就是当关系由多条记录组成时。用你的方法,我同意这是通常的方式,NHibernate将完全控制查询的执行,正如你上面所显示的那样,只是加载每条记录。另一方面,使用我的模型,我可以定位个人记录并进行更明智的查询。另外,这种明确的映射为我的其他报告查询调查这些关系提供了有意义的地方。 – Roman 2011-02-09 03:22:33