学会2与关联的inheritance映射

注:如果我想要的是不可能的,一个“不可能”的答案将被接受

在有关inheritance映射的Doctrine 2文档中 ,它说有两种方法:

  • 单表inheritance(STI)
  • 类表inheritance(CTI)

对于这两个,有警告:

如果您使用STI / CTI实体作为多对一或一对一的实体,则不应将inheritance层次上层的其中一个类用作“targetEntity”,而只能使用那些没有子类的类。 否则,Doctrine不能创build这个实体的代理实例,并总是急切地加载实体。

那么,我该如何继续使用基类(抽象)类的关联inheritance ? (并保持当然的performance)


一个用户有许多Pet (由DogCat扩展的抽象类)。

我想做的事 :

 class User { /** * @var array(Pet) (array of Dog or Cat) */ private $pets; } 

由于在Doctrine文件中的警告,我应该这样做:

 class User { /** * @var array(Dog) */ private $dogs; /** * @var array(Cat) */ private $cats; } 

这很烦人,因为我失去了inheritance的好处!

注意:我没有将映射的Doctrine注释添加到数据库,但您可以理解我的意思

我很累,但是这似乎没什么好说的。

你错过了这个警告的重要一点:

如果您使用STI / CTI实体作为多对一或一对一的实体

在你的例子中,情况并非如此! 如果您没有遗漏这些教义的注释,您可能已经注意到了。

关联User :: pets是OneToMany,而不是[One | Many] ToOne。 一个用户有很多宠物。

反向关联 OneToOne,但它的目标是没有inheritance的用户。

罗宾的答案应该是一个很好的提示 – 你可以loggingSQL查询,看看你的数据库实际上做了什么教义!


性能不佳的情况如下所示:

 abstract class Pet { ... } class Cat extends Pet { ... } class Dog extends Pet { ... } class Collar { /** * @Column(length="16") */ protected $color; /** * ManyToOne(targetEntity="Pet") */ protected $owner; } 

现在,如果你想遍历所有的蓝领,主义会遇到一些麻烦。 它不知道什么类的所有者将是,所以它不能使用代理。 相反,它不得不急切地加载$ owner来查明它是猫还是狗。

这对OneToMany或ManyToMany关系不是问题,因为在这种情况下,延迟加载可以正常工作。 代替一个代理,你会得到一个PersistentCollection。 PersistentCollection始终只是一个PersistentCollection。 它不关心它自己的内容,直到你真正要求他们。 所以懒加载工作正常。

我想你已经误解了,你所引用的手册的一部分标题是“性能影响”,他们并没有告诉你,你不能这样做,只是如果你这样做有性能影响。 这对延迟加载是有意义的 – 对于异构集合的STI实体,您必须先到数据库并加载实体,然后才能知道它将是什么类,所以懒加载是不可能的/没有意义。 我现在正在学自己的学说2,所以我嘲笑你的榜样,下面的作品可以更好的:

 namespace Entities; /** * @Entity * @Table(name="pets") * @InheritanceType("SINGLE_TABLE") * @DiscriminatorColumn(name="pet_type", type="string") * @DiscriminatorMap({"cat" = "Cat", "dog" = "Dog"}) */ class Pet { /** @Id @Column(type="integer") @generatedValue */ private $id; /** @Column(type="string", length=300) */ private $name; /** @ManyToOne(targetEntity="User", inversedBy="id") */ private $owner; } /** @Entity */ class Dog extends Pet { /** @Column(type="string", length=50) */ private $kennels; } /** @Entity */ class Cat extends Pet { /** @Column(type="string", length=50) */ private $cattery; } /** * @Entity * @Table(name="users") */ class User { /** @Id @Column(type="integer") @generatedValue */ private $id; /** @Column(length=255, nullable=false) */ private $name; /** @OneToMany(targetEntity="Pet", mappedBy="owner") */ private $pets; } 

…和testing脚本….

 if (false) { $u = new Entities\User; $u->setName("Robin"); $p = new Entities\Cat($u, 'Socks'); $p2 = new Entities\Dog($u, 'Rover'); $em->persist($u); $em->persist($p); $em->persist($p2); $em->flush(); } else if (true) { $u = $em->find('Entities\User', 1); foreach ($u->getPets() as $p) { printf("User %s has a pet type %s called %s\n", $u->getName(), get_class($p), $p->getName()); } } else { echo " [1]\n"; $p = $em->find('Entities\Cat', 2); echo " [2]\n"; printf("Pet %s has an owner called %s\n", $p->getName(), $p->getOwner()->getName()); } 

我所有的猫狗都是正确的types:

如果你看看生成的SQL,你会注意到,当OneToMany targetEntity是“宠物”时,你得到这样的SQL:

 SELECT t0.id AS id1, t0.name AS name2, t0.owner_id AS owner_id3, pet_type, t0.cattery AS cattery4, t0.kennels AS kennels5 FROM pets t0 WHERE t0.owner_id = ? AND t0.pet_type IN ('cat', 'dog') 

但是,当它被设置为猫,你得到这个:

 SELECT t0.id AS id1, t0.name AS name2, t0.cattery AS cattery3, t0.owner_id AS owner_id4, pet_type FROM pets t0 WHERE t0.owner_id = ? AND t0.pet_type IN ('cat') 

HTH。