3

我想要两个表,Person并且Address,共享它们的主键:

CREATE TABLE Person (id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY);
CREATE TABLE Address (personId INT UNSIGNED NOT NULL PRIMARY KEY);

我试图模拟这样一种想法,即当你创建一个 时Person,你也在Address为它创建一个。

我想出了以下映射:

class Person
{
    /**
     * @Id
     * @Column(type="integer")
     * @GeneratedValue
     */
    private $id;

    /**
     * @OneToOne(targetEntity="Address", cascade={"all"})
     * @JoinColumn(name="id", referencedColumnName="personId")
     */
    private $address;

    public function __construct() {
        $this->address = new Address($this);
    }
}

class Address
{
    /**
     * @Id
     * @OneToOne(targetEntity="Person")
     * @JoinColumn(name="personId", referencedColumnName="id")
     */
    private $person;

    public function __construct(Person $person) {
        $this->person = $person;
    }
}

当我尝试坚持Person时,我希望它级联到Address,但我得到以下异常:

教义\ORM\ORMException

实体类型Address通过外部实体具有身份Person,但是该实体本身没有身份。您必须调用 EntityManager#persist()相关实体并确保在尝试持久之前生成了标识符Address。在 Post Insert ID Generation(例如 MySQLAuto-Increment 或 PostgreSQL SERIAL)的情况下,这意味着您必须EntityManager#flush() 在两个持久化操作之间调用。

我不能显式调用flush(),因为我没有显式persist()调用,Address而是希望Person级联持久化Address

所以据我了解,Doctrine 的工作应该是flush()在适当的时候获得id.

当为and使用不同auto_increment的 id时,这种一对一的关系级联很好(但是我必须添加一个to )。PersonAddressaddressIdPerson

我的目标是让他们共享相同的内容id

那可能吗?

4

3 回答 3

3

如何在学说 2.1 中通过外国实体使用身份?

在您的场景中,您可以使用@ManyToOne关联@OneToOne

编辑

当您保持拥有方时,关联将保持不变。在您的映射中,即Address. 更改您的映射,以便所有者拥有外键,并且一切正常。

/**
 * @OneToOne(targetEntity="Address", cascade={"all"}, inversedBy="person")
 * @JoinColumn(name="id", referencedColumnName="personId")
 */
private $address;

换句话说,如果Address应该是所有者,Person那么您必须首先坚持Address才能坚持关联。如果您希望关联与 保持一致Person,则应将 标记为Person拥有方和方:Address

/**
 * @Id
 * @OneToOne(targetEntity="Person", inversedBy="address")
 * @JoinColumn(name="personId", referencedColumnName="id")
 */
private $person;
于 2013-10-31T16:48:20.517 回答
2

您可以使用symfony 侦听器并从地址实体中删除自动增量策略:

首先创建一个Listner并创建你的listner类,如AdressGenerator.php

<?php

namespace Zodiac\HelperBundle\Listener;


use Doctrine\ORM\Event\LifecycleEventArgs;
use AAA\XXXBundle\Entity\Person;
use AAA\XXXBundle\Entity\Adress;

class AdressGenerator
{
    public function prePersist(LifecycleEventArgs $args)
    {
        // This function is called just beforethe event Persist 
        // The AdressGeneratorlistner is defined in the config.yml file
        // Get the entity manager and the entity from le LifecycleEventArgs
        $em = $args->getEntityManager();
        $entity = $args->getEntity();


        if ($entity instanceof Person) {
            //Create the Adress entity
            $adress = new Adress();
            $adress->setId($entity->getId()); 
            $em->persist($adress);
            $entity->setAdress($adress);
            $em->persist($entity);
            $em->flush();

        }

    }
}

然后在 config.yml 中添加你的监听服务:

services:
AdressGenerator.listener:
    class: AAA\xxxBundle\Listener\AdressGenerator
    tags:
        - { name: doctrine.event_listener, event: prePersist }
于 2014-12-25T20:29:50.117 回答
0

我找到了一个解决方案,但我不知道这是否是最好的(对我来说它看起来很重)。

这不是映射问题,而是我们坚持的方式:

而不是这样做:

$em->persist($person);
$em->flush();

我们应该这样做:

$em->transactional(function($em) use ($person)
{
    $address = $person->getAddress();
    $person->setAddress(null);
    $em->persist($person);
    $em->flush();
    $address->setPerson($person);
    $em->persist($address);
    $em->flush();
});

如果有人有更清洁的解决方案,(s)他会得到赏金:-)。

于 2014-12-25T20:24:47.083 回答