繁体   English   中英

Doctrine 2继承映射与关联

[英]Doctrine 2 Inheritance Mapping with Association

注意:如果我不想要的话,将接受“不可能”的答案

关于继承映射Doctrine 2文档中 ,它说有两种方法:

  • 单表继承(STI)
  • 类表继承(CTI)

两者都有警告:

如果您将STI / CTI实体用作多对一或一对一实体,则不应将继承层次结构的较高级别某个类用作“targetEntity”,而只应使用没有子类的那些类。 否则,Doctrine不能创建该实体的代理实例,并且总是急切地加载该实体。

那么,我如何继续使用与基类(抽象)类的关联继承 (并保持当然的表现)


用户有许多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;
}

这很烦人,因为我放弃了继承的好处!

注意:我没有为映射到DB添加Doctrine注释,但您可以理解我的意思

我很累,但这似乎很无聊。

你错过了那个警告的重要部分:

如果您将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。 在你真正要求它们之前,它并不关心它自己的内容。 所以延迟加载工作正常。

我认为你误解了,你引用的手册部分名为“性能影响”,他们并没有告诉你你不能这样做,只是如果你这样做会影响性能。 这对于延迟加载是有意义的 - 对于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。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM