使用“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的主要内容,如果未能解决你的问题,请参考以下文章
此方法显式使用的 CAS 策略已被 .NET Framework 弃用。若要出于兼容性原因而启用 CAS 策略,请使用 NetFx40_LegacySecurityPolicy 配置开关