我想你可能从错误的角度来看它。在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 Baley和one可能会提供一些关于此主题的帮助。
注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
Em,你说的很对,多对多关系的显式映射很少以这种方式完成,但背后的智慧就是当关系由多条记录组成时。用你的方法,我同意这是通常的方式,NHibernate将完全控制查询的执行,正如你上面所显示的那样,只是加载每条记录。另一方面,使用我的模型,我可以定位个人记录并进行更明智的查询。另外,这种明确的映射为我的其他报告查询调查这些关系提供了有意义的地方。 – Roman 2011-02-09 03:22:33