使用“自动”策略时,用学说明确设置ID


100

我的实体使用以下注释作为其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


您是否使用学说来导入现有记录?
rojoca 2011年

2
埃里克,没关系...我明白您要做什么。您基本上需要一个@GeneratedValue(strategy =“ ItDepends”):)
威尔·摩尔三世

1
需要注意的一件事是,似乎不是“ isPostInsertGenerator” == true的ID生成器将已经运行。您可以在持久化后更改ID的值,但是会丢失序列号。
gview 2012年

15
现在,新的解决方案确实允许我将id设置为一个理论夹具。但是,使用$ metadata-> setIdGeneratorType(\ Doctrine \ ORM \ Mapping \ ClassMetadata :: GENERATOR_TYPE_NONE); 允许设置和保存ID。(MySQL)。
jmoz 2012年

2
该新解决方案在Symfony 3.0中不起作用。我不得不使用$metadata = $this->getEntityManager()->getClassMetaData(User::class); $metadata->setIdGenerator(new AssignedGenerator()); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);
piotrekkr,2016年

Answers:


51

尽管您的解决方案可以在MySQL上正常运行,但是由于它是基于序列的,因此我无法使其与PostgreSQL一起使用。

我必须添加此行以使其完美工作:

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

最好的祝福,


谢谢!自从这是第一个问题以来,Doctrine已有所改善,因此我已经接受了您的回答并相应地更新了我的原始票证。
埃里克

谢谢您,我很乐意为您提供帮助:)
nicolasbui 2012年

2
这将永久设置此发电机吗?我可以添加一条带有强制ID的记录,然后让其使用自动增量ID吗?
Pavel Dubinin 2014年

1
我可以确认这适用于Symfony 3.2。但是我没想到的是,必须执行之后设置生成器$em->persist($entity)
bodo '17

29

也许教义发生了变化,但现在正确的方法是:

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

1
这仍然是相关信息,适用于Doctrine 2.4.1,但是@gphilip提到的第二行应删除。
Mantas

不适用于> 2.5的Doctrine,因为它ClassMetadata是一个接口,因此不能具有任何常量,因此不起作用。
TiMESPLiNTER


@gphilip如果希望它与关联一起使用,第二行很重要。
塔兹

1
可以简化使用$metadata::GENERATOR_TYPE_NONE
fyrye

7

如果实体是类表继承的一部分,则需要为两个实体(您要保留的实体和根实体)更改类元数据中的id生成器


我认为情况是您只需要指定根实体。元数据工厂在确定id策略时会检查继承。
塞斯·巴丁

实际上,当我仅将其添加到根实体时,它就可以正常工作。当我将它们添加到两者时,我会收到SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails错误消息。不满意
ioleo 2014年

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

Doctrine 2.5和MySQL的解决方案

“新解决方案”不适用于Doctrine 2.5和MySQL。您必须使用:

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

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


1

我创建了一个库来为Doctrine实体设置将来的ID。当使用所有排队的ID时,它将恢复为原始ID生成策略,以最大程度地减少影响。对于单元测试来说,这应该是一个简单的方法,这样就不必重复这样的代码。


1

灵感来自Villermen工作中,我创建的库tseho /学说分配的身份,它允许你手动分配ID的学说实体,即使在实体使用策略及DCS AUTO,SEQUENCE,IDENTITY或UUID。

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

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

生成器的替换发生在Doctrine EventListener中,无需在固定装置中添加任何其他代码。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.