32

假设我在数据库中有 2 个表。例如:Dog & Boss 这是多对多的关系,因为老板可以拥有超过 1 只狗,而狗可以拥有超过 1 个主人。我是 Bobby 的所有者,但我的妻子也是。

但是多对多是不允许的,所以有一个helpertable:DogsPerBoss

如何在代码中建模?

Class Boss 可以拥有 Dogs 的集合。Dog 类可以有一个 Boss 集合。--> 至少,我是这么认为的。也许有更好的解决方案?

辅助表中的额外数据怎么样?那应该是de Boss级还是Dog级?eg:昵称(我称狗为“好孩子”,我妻子称他为“doggie”)

我希望我的问题有点清楚?关于实现这一目标的最佳方法是否有任何最佳实践?你能给我一些参考吗?

ORM(如 NHibernate)不是一个选项。

4

12 回答 12

27

为什么说桌子?您是在创建对象模型还是数据库模型?

对于对象模型,没有理由 Dog 不能拥有 aList<Owner>而所有者拥有List<Dog>. 只有在关系上有属性时,才需要中间类(UML 称之为关联类)。那时你会有一个带有额外属性的 DogOwnership 类,每个 Owner 都会有一个List<DogOwnership>,每个 Dog 也有。DogOwner 将具有 Dog、Owner 和额外的属性。

于 2009-07-09T13:03:06.200 回答
17
public class Boss
{
   private string name;
   private List<Hashtable> dogs;
   private int limit;

   public Boss(string name, int dogLimit)
   {
      this.name = name;
      this.dogs = new List<Hashtable>();
      this.limit = dogLimit; 
   }

   public string Name { get { return this.name; } }

   public void AddDog(string nickname, Dog dog)
   {
      if (!this.dogs.Contains(nickname) && !this.dogs.Count == limit)
      {
         this.dogs.Add(nickname, dog);
         dog.AddBoss(this);
      } 
   }

   public void RemoveDog(string nickname)
   {
       this.dogs.Remove(nickname);
       dog.RemoveBoss(this);
   }

   public void Hashtable Dogs { get { return this.dogs; } }
}

public class Dog
{
   private string name;
   private List<Boss> bosses;

   public Dog(string name)
   {
      this.name = name;
      this.bosses = new List<Boss>();
   }

   public string Name { get { return this.name; } }

   public void AddBoss(Boss boss)
   {
      if (!this.bosses.Contains(boss))
      {
          this.bosses.Add(boss);
      }
   }

   public void RemoveBoss(Boss boss)
   {
      this.bosses.Remove(boss);
   }  

   public ReadOnlyCollection<Boss> Bosses { get { return new ReadOnlyCollection<Boss>(this.bosses); } }
}

上面维护了 Boss 可以有多个狗(有限制)和狗有多个 Boss 的关系。这也意味着当老板添加一只狗时,他们可以为这只狗指定一个昵称,该昵称只有该老板独有。这意味着其他老板可以添加相同的狗,但昵称不同。

至于限制,我可能会将其作为 App.Config 值,您在实例化老板对象之前刚刚读入该值。所以一个小例子是:

var james = new Boss("James", ConfigurationManager.AppSettings["DogsPerBoss"]);
var joe = new Boss("Joe", ConfigurationManager.AppSettings["DogsPerBoss"]);

var benji = new Dog("Benji");
var pooch = new Dog("Pooch");

james.AddDog("Good boy", benji);
joe.AddDog("Doggy", benji);

james.AddDog("Rover", pooch);
joe.AddDog("Buddy", pooch);  // won't add as the preset limit has been reached.

您显然可以根据需要进行调整,但是,我认为您正在寻找的基础知识就在那里。

  • 老板可以养多只狗,但有限制
  • 狗可以有多个老板
  • 老板可以对同一只狗有不同的昵称。
于 2009-07-09T13:20:46.113 回答
7

像这样的东西;虽然它仍然需要一些微调(将集合设为私有并为其添加一个只读公共访问器,例如返回一个只读集合,但您会发现偏差。

public class Dog
{
    public List<Boss> Bosses;

    public void AddBoss( Boss b )  
    {
        if( b != null && Bosses.Contains (b) == false )
        {
            Bosses.Add (b);
            b.AddDog (this);
        }
    }

    public void RemoveBoss( Boss b )
    {
         if( b !=null && Bosses.Contains (b) )
         {
             Bosses.Remove (b);
             b.RemoveDog (this);
         }
    }
}

public class Boss
{
    public List<Dog> Dogs;

    public void AddDog( Dog d )
    {
         if( d != null && Dogs.Contains (d) == false )
         {
              Dogs.Add(d);
              d.AddBoss(this);
         }
    }

    public void RemoveDog( Dog d )
    {
        if( d != null && Dogs.Contains(d) )
        {
            Dogs.Remove (d);
            d.RemoveBoss(this);
        }
    }
}

通过这种方式,您可以在代码中建模多对多,其中每个 Dog 都知道他的 Boss,每个 Boss 都知道他的 Dog。当您需要帮助表中的额外数据时,您还需要创建另一个类。

于 2009-07-09T13:06:33.953 回答
2

传统的多对多关系在匹配表上没有额外的字段。

因为您确实有具有独特信息的字段,所以我倾向于停止考虑这些关系的多对多。

一旦您将信息添加到匹配表中,我认为您已经将该表本身变成了一个实体,因此需要它自己的对象来表示它。

此时,您可以开始使用 DogsName 类来连接人和狗——两者都将包含对该对象的引用作为集合的一部分。

但是,您是否给狗起名字以供其称呼或拥有狗是独立的。

除了根据不同的人对狗名的关系进行建模外,您还需要对所有权关系进行建模。在内存中,这意味着两个对象都包含其他对象的列表。

于 2009-07-09T13:06:24.907 回答
1

如果您不需要记录昵称,那么 Dog 应该有一个 Boss 列表,而 Boss 应该有一个 Dogs 列表。

如果 Dog 和 Boss 之间的关系具有属性,在本例中为昵称,那么您应该创建一个类来表示该关系并让 Dog 和 Boss 都保存该类型的列表。

我已经使用NHibernate一段时间了,发现它对于缓解这种 对象关系阻抗不匹配非常有用。

于 2009-07-09T13:00:37.883 回答
1

这是多对多不起作用的数据库之间的经典问题,因此您的帮助表,以及多对多工作正常的对象世界。一旦关系具有属性,那么您应该创建一个新类来保存该信息。但是,如果您查看对象关系映射 - ORM - 整个领域的发展是为了解决 DB 和 Object 之间的这个(以及许多其他)问题,您将节省大量的时间。

于 2009-07-09T13:05:06.497 回答
1

如果您有一个简单的多对多链接表,其中包含关系中每个表的外键,那么您可以按照您的建议对其进行建模:Boss 有一个 Dogs 集合,Dog 有一个 Bosses 集合。

如果您与额外数据(例如昵称)具有多对多关系,那么您可以将其建模为两个一对多关系。创建一个实体,例如 DogBoss,以便 Boss 有一个 DogBoss 的集合,而 Dog 有一个 DogBoss 的集合。

于 2009-07-09T13:05:25.217 回答
1

我是否遗漏了某些东西,或者您需要的唯一代码如下:

List<Bosses> BossList;

class Dog {}
class Boss { Dog[] Dogs; }

您不需要显式地为双向关系建模。它隐含在代码结构中。这样做可能还有其他原因,但一般来说,有一个单向引用和一种遍历引用对象集的方法就足够了。

于 2009-07-09T13:30:03.637 回答
0

我想我错过了一些东西。为什么不允许多对多?

public class Boss
{
    Dog[] dogs;
}

public class Dog
{
    Boss[] bosses;
}
于 2009-07-09T13:03:18.603 回答
0

在关系模型中,建模多对多关系的最佳方法(使用您的 Dogs/Bosses 示例)是拥有三个单独的表。

一张表用于 DOGS,一张表用于 BOSSES(这些表中的每一个都有唯一的键),第三张表通常是“联结表”。

此表通常至少有两个字段,一个字段用于 Dog 的外键,另一个字段用于 Boss 的外键。这样每条Dog可以有很多boss,每条Boss也可以有很多Dog。

现在,当涉及到以更加面向对象的方式在代码中对此进行建模时,这通常是通过拥有一个 Dog 类和一个 Boss 类来实现的。除了这些对象中的每一个都具有通常的原子属性外,每个对象还会公开一个属性,该属性是另一个对象的集合。

因此,例如,Dog 对象将具有名为“Bosses”的属性。此属性将公开分配给特定 Dog 对象(如联结表中定义)的 Boss 对象的集合,另一方面,每个 Boss 对象将公开一个名为 Dogs 的属性,该属性将是分配的 Dog 对象的集合到那个特定的 Boss 对象(由联结表定义)。

请注意,这些对象中可能存在一些“重叠”(即,一个“狗”对象可能具有另一个“狗”对象具有的“老板”对象),但是,这是转换三表多表的传统机制对多关系模型转换为面向对象模型。

于 2009-07-09T13:08:51.367 回答
0

每次我们都需要考虑现实生活和我们的需求。在这种情况下,关键是哪个应该有另一个。

在现实生活中,狗和老板可能没有彼此。但是您的软件需求应该会影响这种关系。

  • 例如,如果您正在开发兽医患者管理软件,对于治疗流浪狗的兽医,那么患者(狗)-监护人(老板)的关系应该是这样的:老板必须至少有一只狗,而狗可能没有老板(那么boss id是这个关系的外键)这意味着在你的设计中狗类必须拥有一个boss的集合。为什么,因为没有任何狗就无法创建任何老板实例。我们也可以通过数据逻辑来做出这个决定。让我们考虑一下您何时尝试将您的狗和老板类保存在数据库中。如果关系条件如上,在保存老板时,您应该在联结表中插入联结记录。

  • 如果你开发那个软件是一个不治疗流浪狗的兽医,那么患者-父母关系需要是这样的:一只狗必须至少有一个老板,老板必须至少有一只狗,我们需要考虑这种特殊的关系案子。这意味着这些类实例中的任何一个都不能在没有彼此的情况下创建。所以我们需要在我们的OO设计中定义这个专业。这意味着我们需要一个代表这种依赖关系的类。当然,这种依赖关系将存储在联结表中。

- 如果你的软件是为治疗流浪狗的兽医而开发的,而这些狗被老板收养,你的设计应该是这样的:任何狗都可能没有老板,任何老板在收养之前都可能没有任何狗。在这种情况下,我们的 OO 设计需要关心这种特殊情况。这种情况与第一种情况有点相同。所以我们可以将任何类的集合添加到另一个类中。但是,任何这样的软件需求都会影响其他需求。比如报道。如果兽医担心老板收养的狗,他迟早会要求报告谁收养了哪只狗。就像句子中一样,(老板收养的狗)如果狗类包含老板类的集合会更好。

我希望我能正确回答你的问题。

于 2010-02-06T03:16:02.030 回答
-1

不知道你的要求是什么。但这是您想要的表结构:

狗桌

DOG_ID int PK DOG_Name varchar(50)

DogsPerBoss

ID int DOG_ID int BOSS_ID int DogNickName varchar(15)

老板

BOSS_ID int PK BOSS_Name varchar(50)

于 2009-07-09T13:02:49.320 回答