使用“AUTO”策略时使用 Doctrine 显式设置 Id

Posted

技术标签:

【中文标题】使用“AUTO”策略时使用 Doctrine 显式设置 Id【英文标题】:Explicitly set Id with Doctrine when using "AUTO" strategy 【发布时间】:2011-07-15 03:36:01 【问题描述】:

我的实体使用这个注解作为它的 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

【问题讨论】:

您是否使用学说导入现有记录? 埃里克,没关系...我明白你在做什么。你基本上需要一个@GeneratedValue(strategy="ItDepends") :) 对此需要注意的一点是,似乎不是“isPostInsertGenerator”== true 的 Id 生成器已经运行。你可以在持久化后更改 ID 的值,但是会丢失一个序列号。 新的解决方案现在允许我在教义夹具中设置 id。但是,使用 $metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_NONE);允许设置和保存 id。 (MySQL)。 新的解决方案在 Symfony 3.0 中不起作用。我不得不使用$metadata = $this->getEntityManager()->getClassMetaData(User::class); $metadata->setIdGenerator(new AssignedGenerator()); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE); 【参考方案1】:

虽然您的解决方案适用于 MySQL,但我未能使其适用于 PostgreSQL,因为它是基于序列的。

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

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

【讨论】:

谢谢!自从这是第一个问题以来,教义有所改进,因此我接受了您的回答并相应地更新了我的原始票。 谢谢你,我很乐意尽我所能提供帮助:) 这会永久设置此生成器吗?我可以添加一条强制 ID 的记录,然后让它使用自动增量 ID 吗? 我可以确认这适用于 Symfony 3.2。然而我没想到的是,生成器必须在执行$em->persist($entity)之后设置。【参考方案2】:

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

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

【讨论】:

这仍然是相关信息,适用于 Doctrine 2.4.1,但应删除@gphilip 提到的第二行。 不适用于 Doctrine >2.5,因为ClassMetadata 是一个接口,因此不能有任何常量。 有类ClassMetadata @gphilip 第二行很重要,如果你想要它work with associations。 可以使用$metadata::GENERATOR_TYPE_NONE进行简化【参考方案3】:

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

【讨论】:

我认为情况是你只需要指定根实体。 metadatafactory 在确定 id 策略时检查继承。 事实上,当我只将它添加到根实体时,它可以完美地工作。当我将它添加到两者时,我收到SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails 错误。否决【参考方案4】:

仅当所有实体在插入之前都有 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();
    

【讨论】:

【参考方案5】:

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。

【讨论】:

【参考方案6】:

我创建了a library 来为 Doctrine 实体设置未来的 ID。当所有排队的 ID 都被消耗时,它会恢复到原始 ID 生成策略,以最大程度地减少影响。它应该是一个简单的单元测试插件,这样就不必重复这样的代码了。

【讨论】:

【参考方案7】:

受 Villermen 工作的启发,我创建了库 tseho/doctrine-assigned-identity,它允许您手动为 Doctrine 实体分配 ID,即使该实体使用 AUTO、SEQUENCE、IDENTITY 或 UUID 状态也是如此。

不应该在生产中使用它,但它对功能测试非常有用。

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

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

【讨论】:

以上是关于使用“AUTO”策略时使用 Doctrine 显式设置 Id的主要内容,如果未能解决你的问题,请参考以下文章

原子部署期间的Doctrine元数据缓存

开放策略代理 - CI 管道中的显式逻辑与

Doctrine DQL:如何使表达式反转

Doctrine 2 ORM 级联删除相关实体

此方法显式使用的 CAS 策略已被 .NET Framework 弃用。若要出于兼容性原因而启用 CAS 策略,请使用 NetFx40_LegacySecurityPolicy 配置开关

在 AWS 中缩减 Auto Scaling 组会导致永久警报