注意:如果我不想要的话,将接受“不可能”的答案
在Doctrine 2 documentation about inheritance mapping中,它说有两种方式:
两者都有警告:
如果您将STI / CTI实体用作多对一或一对一实体,则不应使用继承层次结构的上层中的一个类作为“ targetEntity“,只有没有子类的那些。否则,Doctrine不能创建该实体的代理实例,并且总是急切地加载该实体。
那么,我如何继续使用继承与基础(抽象)类的关联? (并保持当然的表现)
用户有许多Pet
(由Dog
或Cat
扩展的抽象类)。
我想做什么:
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;
}
这很烦人,因为我放弃了继承的好处!
注意:我没有为映射到DB添加Doctrine注释,但你可以理解我的意思
答案 0 :(得分:47)
我很累,但这似乎很无聊。
你错过了那个警告的重要部分:
如果您将STI / CTI实体用作多对一或一对一实体
在你的例子中并非如此!如果你没有省略学说注释,你可能已经注意到了。
User :: pets的关联是OneToMany,而不是[One | Many] ToOne。一个用户有很多宠物。
反向关联是 OneToOne,但它的目标是User,它没有继承。
Robin的答案应该是一个很好的提示 - 你可以记录sql查询,看看你的数据库实际上做了什么!
糟糕的表现方案如下:
abstract class Pet { ... }
class Cat extends Pet { ... }
class Dog extends Pet { ... }
class Collar {
/**
* @Column(length="16")
*/
protected $color;
/**
* ManyToOne(targetEntity="Pet")
*/
protected $owner;
}
现在,如果你想迭代所有的蓝色项圈,Doctrine会遇到麻烦。它不知道$ owner所属的类,因此它不能使用代理。相反,它被迫急切地加载$ owner来查明它是猫还是狗。
对于OneToMany或ManyToMany关系,这不是问题,因为在这种情况下,延迟加载工作正常。您将获得PersistentCollection而不是代理。而PersistentCollection总是只是一个PersistentCollection。在你真正要求它们之前,它并不关心它自己的内容。所以延迟加载工作正常。
答案 1 :(得分:46)
我认为你误解了,你引用的手册部分名为“性能影响”,他们并没有告诉你不能这样做,只有有如果你这样做,性能影响。这对于延迟加载是有意义的 - 对于STI实体的异构集合,您必须先去数据库并在知道它将成为什么类之前加载实体,因此延迟加载是不可能的/没有意义。我现在正在学习Doctrine 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;
}
...和测试脚本......
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());
}
我所有的猫狗都是正确的类型:
如果查看生成的SQL,您会注意到当OneToMany targetEntity为“pet”时,您会得到如下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')
但是当它被设置为Cat时,你会得到这个:
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。