Explicitly set Id with Doctrine when using “AUTO”

2019-01-10 02:07发布

My entity uses this annotation for it's ID:

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

From a clean database, I'm importing in existing records from an older database and trying to keep the same IDs. Then, when adding new records, I want MySQL to auto-increment the ID column as usual.

Unfortunately, it appears Doctrine2 completely ignores the specified ID.


New Solution

Per recommendations below, the following is the preferred solution:

$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());

Old Solution

Because Doctrine pivots off of the ClassMetaData for determining the generator strategy, it has to be modified after managing the entity in the EntityManager:

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

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

$this->em->flush();

I just tested this on MySQL and it worked as expected, meaning Entities with a custom ID were stored with that ID, while those without an ID specified used the lastGeneratedId() + 1.

7条回答
何必那么认真
2楼-- · 2019-01-10 02:42

In case the entity is part of a class table inheritance you need to change the id-generator in the class metadata for both entities (the entity you are persisting and the root entity)

查看更多
我欲成王,谁敢阻挡
3楼-- · 2019-01-10 02:45

Inspired by Villermen work, I created this library which allows you manually assign IDs to a Doctrine entity, even when the entity uses the stategies AUTO, SEQUENCE, IDENTITY or UUID.

You shouldn't use it in production.

The library will detect automatically the entities with an assigned id and replace the generator only when needed. The library will fallback on the initial generator when an instance does not have an id.

The replacement of the generator occurs in a Doctrine EventListener, no need to add any additional code in your fixtures.

查看更多
狗以群分
4楼-- · 2019-01-10 02:48

Although your solution work fine with MySQL, I failed to make it work with PostgreSQL as It's sequence based.

I've to add this line to make it work perfectly :

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

Best regards,

查看更多
爱情/是我丢掉的垃圾
5楼-- · 2019-01-10 02:49

Perhaps what doctrine changed but now right way is:

$metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_NONE);
查看更多
6楼-- · 2019-01-10 02:54

New solution works fine only when ALL entities have id before insert. When one entity has ID and another one does not - new solution is failing.

I use this function for import all my data:

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();
    }
}
查看更多
Luminary・发光体
7楼-- · 2019-01-10 02:55

Solution for Doctrine 2.5 and MySQL

The "New solution" doesn't work with Doctrine 2.5 and MySQL. You have to use:

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

However I can only confirm that for MySQL,because I haven't tried any other DBMS yet.

查看更多
登录 后发表回答