Явно встановлюйте Id з Doctrine при використанні стратегії "AUTO"


100

Моя організація використовує цю примітку для свого ідентифікатора:

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

З чистої бази даних я імпортую наявні записи зі старої бази даних і намагаюся зберегти ті самі ідентифікатори. Потім, додаючи нові записи, я хочу, щоб MySQL автоматично збільшував стовпчик ідентифікаторів, як зазвичай.

На жаль, схоже, що Doctrine2 повністю ігнорує вказаний ідентифікатор.


Нове рішення

Згідно з наведеними нижче рекомендаціями, найкращим рішенням є наступне:

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

Старе рішення

Оскільки Доктрина відхиляє 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, і він працював як слід, тобто об'єкти зі спеціальним ідентифікатором зберігалися з цим ідентифікатором, тоді як ті, хто не вказав ідентифікатор, використовували lastGeneratedId() + 1.


Чи використовуєте ви доктрину для імпорту існуючих записів?
rojoca

2
Еріку, не маю на увазі ... Я бачу, що ти намагаєшся зробити. Вам в основному потрібен @GeneratedValue (стратегія = "ItDepends") :)
Віль Мур III

1
Що слід зазначити з цього приводу, так це те, що, здається, генератори ідентифікаторів, які не є "isPostInsertGenerator" == true, вже будуть працювати. Ви можете змінити значення ідентифікатора після збереження, проте втратите порядковий номер.
gview

15
Нове рішення тепер дозволяє мені встановити ідентифікатор в кріпленні доктрини. Однак, використовуючи $ metadata-> setIdGeneratorType (\ Doctrine \ ORM \ Mapping \ ClassMetadata :: GENERATOR_TYPE_NONE); дозволяє встановити та зберегти ідентифікатор. (MySQL).
jmoz

2
Це нове рішення не працює в Symfony 3.0. Мені довелося скористатися$metadata = $this->getEntityManager()->getClassMetaData(User::class); $metadata->setIdGenerator(new AssignedGenerator()); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);
piotrekkr

Відповіді:


51

Хоча ваше рішення добре працює з MySQL, мені не вдалося змусити його працювати з PostgreSQL, як це засновано на послідовності.

Я повинен додати цей рядок, щоб він працював ідеально:

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

З найкращими побажаннями,


Дякую! Вчення трохи покращилось, оскільки це було першим питанням, тому я прийняв вашу відповідь і відповідно оновив свій оригінальний квиток.
Ерік

Дякую, і я радий трохи допомогти :)
nicolasbui

2
це встановить цей генератор назавжди? Чи можу я додати один запис із примусовим ідентифікатором, а потім дозволити йому використовувати ідентифікатори автоінкременту?
Павло Дубінін

1
Я можу підтвердити, що це працює з Symfony 3.2. Однак я не очікував, що генератор повинен бути встановлений після виконання $em->persist($entity).
BODO

29

Можливо, що вчення змінилося, але тепер правильний шлях:

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

1
Це все ще актуальна інформація і працює для Доктрини 2.4.1, але другий рядок, як згадував @gphilip, слід видалити.
Mantas

Не працює для Doctrine> 2.5, тому що ClassMetadataце інтерфейс і для цього не може бути констант.
TiMESPLiNTER


@gphilip Другий рядок важливий, якщо ви хочете, щоб він працював з асоціаціями .
Таз

1
Можна спростити, скориставшись$metadata::GENERATOR_TYPE_NONE
fyrye

7

Якщо об'єкт є частиною спадкування таблиці класів, вам потрібно змінити генератор ідентифікаторів у метаданих класу для обох об'єктів (сутність, яку ви зберігаєте, і кореневу сутність)


Я думаю, що випадок полягає в тому, що вам потрібно лише вказати кореневу сутність. Метадані перевіряють спадщину при визначенні стратегії id.
Сет Беттін

Насправді, коли я додаю його лише до кореневої сутності, він працює бездоганно. Коли я додаю їх до обох, я отримую SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint failsпомилки. Проголосовано проти
ioleo

5

Нове рішення працює добре лише тоді, коли ВСІ об'єкти мають ідентифікатор перед вставкою. Якщо одна особа має ідентифікатор, а інша - ні, нове рішення не вдається.

Я використовую цю функцію для імпорту всіх своїх даних:

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, оскільки я ще не пробував жодної іншої СУБД.


1

Я створив бібліотеку для встановлення майбутніх ідентифікаторів для суб'єктів Доктрини. Він повертається до оригінальної стратегії генерації ідентифікаторів, коли всі ідентифікатори в черзі використовуються для мінімізації впливу. Для одиничних тестів це повинно бути легким, щоб такий код не повторювався.


1

Надихнувшись роботою Віллермена , я створив бібліотеку цехо / доктрини-призначеної ідентичності, яка дозволяє вам вручну присвоювати ідентифікатори суб'єкту Доктрини, навіть коли організація використовує стаціонари AUTO, ПОЛІТ, ІДЕНТИЧНІСТЬ або UUID.

Ви ніколи не повинні використовувати його у виробництві , але це дійсно корисно для функціональних тестів.

Бібліотека автоматично виявить об'єкти з присвоєним ідентифікатором та замінить генератор лише за потреби. Коли бібліотека не має присвоєного ідентифікатора, бібліотека повернеться до початкового генератора.

Заміна генератора відбувається в Doctrine EventListener, не потрібно додавати будь-який додатковий код у ваші світильники.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.