使用“自动”策略时,使用Doctrine明确设置Id

时间:2011-03-14 16:13:07

标签: doctrine doctrine-orm

我的实体使用此注释作为其ID:

/**
 * @orm:Id
 * @orm:Column(type="integer")
 * @orm:GeneratedValue(strategy="AUTO")
 */
protected $id;

从干净的数据库中,我从旧数据库导入现有记录并尝试保留相同的ID。然后,在添加新记录时,我希望MySQL像往常一样自动增加ID列。

不幸的是,Doctrine2似乎完全忽略了指定的ID。


新解决方案

根据以下建议,以下是首选解决方案:

$this->em->persist($entity);

$metadata = $this->em->getClassMetaData(get_class($entity));
$metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_NONE);
$metadata->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator());

旧解决方案

因为Doctrine偏离ClassMetaData来确定生成器策略,所以在管理EntityManager中的实体之后必须对其进行修改:

$this->em->persist($entity);

$metadata = $this->em->getClassMetaData(get_class($entity));
$metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_NONE);

$this->em->flush();

我刚刚在MySQL上对此进行了测试,它按预期工作,这意味着具有自定义ID的实体与该ID一起存储,而没有指定ID的实体使用lastGeneratedId() + 1

7 个答案:

答案 0 :(得分:45)

虽然您的解决方案可以正常使用MySQL,但我无法使用PostgreSQL,因为它是基于序列的。

我要添加这一行以使其完美运行:

$metadata->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator());

致以最诚挚的问候,

答案 1 :(得分:28)

也许是什么教义改变了,但现在正确的方法是:

$metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_NONE);

答案 2 :(得分:7)

如果实体是class table inheritance的一部分,您需要更改两个实体(您持久存储的实体和根实体)的类元数据中的id-generator < / p>

答案 3 :(得分:5)

只有在插入前所有实体都具有id时,新解决方案才能正常工作。当一个实体具有ID而另一个实体没有ID时 - 新解决方案失败。

我使用此功能导入我的所有数据:

function createEntity(\Doctrine\ORM\EntityManager $em, $entity, $id = null)
{
    $className = get_class($entity);
    if ($id) {
        $idRef = new \ReflectionProperty($className, "id");
        $idRef->setAccessible(true);
        $idRef->setValue($entity, $id);

        $metadata = $em->getClassMetadata($className);
        /** @var \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata */
        $generator = $metadata->idGenerator;
        $generatorType = $metadata->generatorType;

        $metadata->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator());
        $metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_NONE);

        $unitOfWork = $em->getUnitOfWork();
        $persistersRef = new \ReflectionProperty($unitOfWork, "persisters");
        $persistersRef->setAccessible(true);
        $persisters = $persistersRef->getValue($unitOfWork);
        unset($persisters[$className]);
        $persistersRef->setValue($unitOfWork, $persisters);

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

        $idRef->setAccessible(false);
        $metadata->setIdGenerator($generator);
        $metadata->setIdGeneratorType($generatorType);

        $persisters = $persistersRef->getValue($unitOfWork);
        unset($persisters[$className]);
        $persistersRef->setValue($unitOfWork, $persisters);
        $persistersRef->setAccessible(false);
    } else {
        $em->persist($entity);
        $em->flush();
    }
}

答案 4 :(得分:3)

Doctrine 2.5和MySQL的解决方案

&#34;新解决方案&#34;不适用于Doctrine 2.5和MySQL。你必须使用:

$metadata = $this->getEntityManager()->getClassMetaData(Entity::class);
$metadata->setIdGenerator(new AssignedGenerator());
$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_‌​NONE);

但是我只能对MySQL进行确认,因为我还没有尝试过任何其他DBMS。

答案 5 :(得分:1)

I have created a library to set future IDs for Doctrine entities. It reverts to the original ID generation strategy when all queued IDs are consumed to minimize impact. It should be an easy drop-in for unit tests so that code like this doesn't have to be repeated.

答案 6 :(得分:1)

Villermen的启发,我创建了一个库tseho/doctrine-assigned-identity,即使您使用状态策略AUTO,SEQUENCE,IDENTITY或UUID,该库也可以让您手动将ID分配给Doctrine实体。

永远不要在生产中使用它,但这对于功能测试确实有用。

该库将自动检测具有指定ID的实体,并仅在需要时替换生成器。 当实例没有分配的ID时,该库将退回到初始生成器上。

发生器的替换发生在Doctrine EventListener中,无需在灯具中添加任何其他代码。