关于多对多关系的OO问题(为NHibernate进行规划)

4

我正在设计一个OO对象模型,计划使用NHibernate作为数据访问层。

我想知道在处理彼此具有多对多关系的两个实体时,最好的OO设计是什么(特别是为了方便与NHibernate集成)。

对象: 用户 - 单个用户可以与多个主题相关联 主题 - 单个主题可以与多个用户相关联

在SQL中,这种关系很直观,可以使用多对多表;

tblUser
  userID

tblSubject
  subjectID

tblUserSubject
  userSubjectID
  userID
  subjectID

那么,如何创建“纯”对象呢?每个对象是否应包含其他对象的集合?例如:
class User 
{
   public int userID {get; set;}
   public List<Subject> subjects {get; set;}
}

class Subject
{
   public int subjectID {get; set;}
   public List<User> users {get; set;}
}

有没有更好的方法来建模以便NHibernate可以轻松地持久化关系?
7个回答

3
我没有关于哪种设计最适合NHibernate的答案,但我想评论一下,因为这让我想起了Rob Conery在Hanselminutes上讨论领域驱动设计的内容。
我的直觉反应是,如果你的“纯”用户同时包含主题,而你的主题又包含用户,那么有些地方就不对了。我想把它缩小到一个或另一个。有趣的是,Conery正在讨论他的商店应用程序以及关于产品是否具有类别或类别是否具有产品的争议。结果证明,实际上这都不是问题——它实际上是一种最好由应用程序中的服务处理的联系。(至少,这是最好的DDD实现方式,因为这是他的客户联系实体的方式。)
因此,除了DDD之外,我想知道它是否会帮助您仔细查看用户和主题之间的真正“纯”关系,以及它们是否相互包含。由于我不太喜欢它们互相包含的想法,所以我可能会考虑哪个子集合经常使用。在检索用户时,您是否经常使用其专题?相反,当涉及到主题对象时,是否经常使用此集合中的用户?如果不是,也许这个集合并不直接属于它。
再次强调,我不能证明哪种设计最适合NHibernate,但我认为这值得考虑。

1
发现一篇你可能会觉得有用的文章。 http://codebetter.com/blogs/peter.van.ooijen/archive/2008/05/29/nhibernate-many-to-many-collections-or-mapping-is-not-one-table-one-class.aspx 总结一下,你的解决方案看起来非常相似。为了完整起见,这是 nHibernate 映射文件可能看起来像你的用户对象。使用 nHibernate 映射集合来处理你的用户和主题列表。
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="YourAssemblyName" namespace="YourNamespace">
  <class name ="User" table="Users" proxy="User">
    <id name="Id" type="Int32" column="idUser">
      <generator class="identity"></generator>
    </id>                
    <many-to-one name="CreatedBy" class="User" column="idUser"></many-to-one>
    <bag name="subjects" table="tblUserSubject" lazy="false" >
      <key column="idUser"></key>
      <many-to-many class="Subject" column="idSubject"></many-to-many>
    </bag>
  </class>
</hibernate-mapping> 

subjects是一个使用tblUserSubject表的包。这个链接表的关键是idUser(假设这是您的标识列名称)。多对多类是我们刚刚映射的Subject类。


1

1
如果一个用户拥有对他们所有学科的引用,而一个学科拥有对所有用户的引用,那么当您阅读单个用户时,您需要:
  • 阅读该用户的详细信息;
  • 阅读该用户的学科;
  • 阅读每个学科的用户;
  • 阅读每个学科的用户的学科;
  • 阅读每个学科的用户的学科的用户;
  • [...]

除非您非常注意懒加载,否则您可能会加载两个表的大块内容,以查看单个用户记录。

我以前没有使用过NHibernate,但在Java Hibernate中,您可以通过以下方式解决此问题:

  • 使用懒加载,或
  • 用以下方法替换多对多关系:
    • 两个新类,UserSummarySubjectSummary,绑定到相同的表格,但没有多对一的关系,并且
    • User更改为引用一堆SubjectSummarySubject引用一堆UserSummary

0

您已经建立了支持多对多的基本结构。不要忘记添加方法以正确连接关联:

class User 
{
   public int userID {get; set;}
   public List<Subject> subjects {get; set;}

   public void AddSubject(Subject subject)
   {
       subject.Users.Add(this);
       this.Subjects.Add(subject);
   }
}

class Subject
{
   public int subjectID {get; set;}
   public List<User> users {get; set;}

   public void AddUser(User user)
   {
       user.Subjects.Add(this);
       this.Users.Add(user);
   }
}

0
今天我在Kuate等人的《NHibernate实战》中发现了以下内容(第60页和61页;我更改了书中的对象名称以保持我的原始示例完整)。
这与Vijay的答案类似,但看起来NHiberate更喜欢看到接口而不是列表类型的集合。
class User 
{
   public int userID {get; set;}
   private ISet subjects = new HashedSet();

   public ISet subjects {
      get { return subjects; }
      set { subjects = value; }
   }

   public void AddSubject(Subject subject)
   {
       subject.Users.Add(this);
       this.Subjects.Add(subject);
   }
}

class Subject
{
   public int subjectID {get; set;}
   private ISet users = new HashedSet();

   public ISet users {
      get { return users; }
      set { users = value; }
   }

   public void AddUser(User user)
   {
       user.Subjects.Add(this);
       this.Users.Add(user);
   }
}

0

这应该可以解决问题,但具体取决于您是否实际需要知道哪些用户可用于主题或哪些主题适用于用户,可能可以省略一个属性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接