当该关系具有属性时,如何将类映射到同一类的其他实例?
我有一个名为Person的类,它被映射到表Person
PersonID PersonName PersonAge
----------------------------------
1 Dave Dee 55
2 Dozy 52
3 Beaky 45
4 Mick 55
5 Tich 58
我希望使用名为PersonPerson的连接表在Person和Person之间建立多对多的关系:
PersonPersonID PersonID RelatedPersonID RelationshipID
--------------------------------------------------------
1 1 5 1
2 3 4 2
3 2 1 3
我想在PersonPerson表中使用以下属性:
RelationshipID RelationshipName
--------------------------------
1 Colleague
2 Manager
3 Tutor
This question和链接到post by Billy McCafferty解释了由于PersonPerson表中的其他列,PersonPerson关系必须从正常的JOIN升级到实体本身。但是它没有解释什么时候它是自我加入。不同之处在于,如果我要求所有相关人员 Dave Dee (ID = 1),我不仅应该 Tich (ID = 5),而且我应该get也得到 Dozy (ID = 2),因为Dave Dee也在RelatedPersonID列中。
到目前为止,我的解决方案是在Person类中有两个属性。
public virtual IList<PersonPerson> PersonPersonForward {get;set;}
public virtual IList<PersonPerson> PersonPersonBack {get;set;}
private List<PersonPerson> personPersonAll;
public virtual List<PersonPerson> PersonPersonAll
{
get
{
personPersonAll = new List<PersonPerson>(PersonPersonForward);
personPersonAll.AddRange(PersonPersonBack);
return personPersonAll;
}
}
在hbm中有以下内容:
<bag name="PersonPersonForward" table="PersonPerson" cascade="all">
<key column="PersonID"/>
<one-to-many class="PersonPerson" />
</bag>
<bag name="PersonPersonBack" table="PersonPerson" cascade="all">
<key column="RelatedPersonID"/>
<one-to-many class="PersonPerson" />
</bag>
这似乎有点笨拙而且不优雅。 NHibernate通常为大多数日常问题提供优雅的解决方案。上述是明智的做法还是有更好的方式?
答案 0 :(得分:2)
我想我也会这样做,但是,我觉得像这样建模它有点“笨拙”。 我的意思是:你有一个与某个人有关系的人的集合,但你也有一个“背关系” 这真的有必要吗?是不是删除这个反向集合的选项,而是在PersonRepository上指定一个方法,它可以给你所有与某个人有某种关系的人?
嗯,这可能听起来有点模糊,所以这里有一些代码(请注意,为了简洁起见,我省略了'虚拟'修饰符等...(我也不想使用那些修饰符,所以在99%的时间里,我在我的类映射中指定'lazy = false'。public class Person
{
public int Id {get; set;}
public string Name {get; set;}
public IList<PersonPerson> _relatedPersons;
public ReadOnlyCollection<PersonPerson> RelatedPersons
{
get
{
// The RelatedPersons property is mapped with NHibernate, but
// using its backed field _relatedPersons (can be done using the
// access attrib in the HBM.
// I prefer to expose the collection itself as a readonlycollection
// to the client, so that RelatedPersons have to be added through
// the AddRelatedPerson method (and removed via a RemoveRelatedPerson method).
return new List<PersonPerson) (_relatedPersons).AsReadOnly();
}
}
public void AddRelatedPerson( Person p, RelationType relatesAs )
{
...
}
}
如您所见,Person类只剩下一个集合,它是PersonPerson对象的集合,表示此Person具有的关系。 为了获得与给定Person有关系的Persons,您可以在PersonRepository上创建一个返回那些Persons的特定方法,而不是将它们放在Person类的集合中。我认为这也会提高性能。
public class NHPersonRepository : IPersonRepository
{
...
public IList<Person> FindPersonsThatHaveARelationShipWithPerson( Person p )
{
ICriteria crit = _session.CreateCriteria <Person>();
crit.AddAlias ("RelatedPersons", "r");
crit.Add (Expression.Eq ("r.RelatedWithPerson", p));
return crit.List();
}
}
'back-reference'不是Person类的成员;它必须通过存储库访问。 这也是Eric Evans在他的DDD书中所说的:在某些情况下,最好在存储库上有一个专门的方法,可以让你访问相关的对象,而不是让它们(=相关的对象)随身携带与对象本身。
我没有测试代码,我只是在这里输入,所以我也没有检查语法错误等等......但我认为应该澄清一下我将如何看待它。
答案 1 :(得分:2)
在我看来,您基本上构建了一个directed graph的模型,两个映射PersonPersonForward
和PersonPersonBack
分别代表了传出边和传入边。
你的关系类型的语义强化了这种指向性:虽然是的同事 - 很可能是symmetric relation,是经理 - 和 is-a-Tutor-of 几乎肯定是不对称的。
我认为在这种情况下,数据模型试图告诉您,两个链接集合虽然是兼容类型,但在上下文中并不相同。