"AUTO"전략을 사용할 때 Doctrine을 사용하여 명시 적으로 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
에릭, 신경 쓰지 마 ... 뭘 하려는지 알 겠어. 당신은 기본적으로 (전략 = "ItDepends") : @GeneratedValue 필요
윌 무어 III에게

1
이것에 대해 주목해야 할 점은 "isPostInsertGenerator"== true가 아닌 Id 생성기가 이미 실행 된 것으로 보인다는 것입니다. 지속 후 ID 값을 변경할 수 있지만 시퀀스 번호가 손실됩니다.
gview 2011

15
이제 새로운 솔루션을 통해 교리 고정 장치에 ID를 설정할 수 있습니다. 그러나 $ metadata-> setIdGeneratorType (\ Doctrine \ ORM \ Mapping \ ClassMetadata :: GENERATOR_TYPE_NONE); ID를 설정하고 저장할 수 있습니다. (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
이 발전기가 영구적으로 설정됩니까? 강제 ID로 하나의 레코드를 추가 한 다음 자동 증가 ID를 사용하도록 할 수 있습니까?
Pavel Dubinin 2014-06-25

1
이것이 Symfony 3.2에서 작동하는지 확인할 수 있습니다. 그러나 내가 예상하지 못한 것은 실행 생성기를 설정해야한다는 것입니다 $em->persist($entity).
bodo

29

아마도 교리가 바뀌었지만 이제 올바른 방법은 다음과 같습니다.

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

1
이것은 여전히 ​​관련 정보이며 Doctrine 2.4.1에서 작동하지만 @gphilip이 언급 한 두 번째 줄은 제거해야합니다.
Mantas

ClassMetadata은 인터페이스이고 상수를 가질 수 없기 때문에 Doctrine> 2.5에서는 작동 하지 않습니다.
TiMESPLiNTER


@gphilip 두 번째 줄은 연결과 함께 작동하도록 하려는 경우 중요합니다 .
Taz

1
사용하여 단순화 할 수 있습니다$metadata::GENERATOR_TYPE_NONE
fyrye을

7

엔터티가 클래스 테이블 상속의 일부인 경우 엔터티 (지속중인 엔터티 및 루트 엔터티) 에 대한 클래스 메타 데이터에서 id-generator를 변경해야합니다.


루트 엔터티 만 지정하면되는 경우라고 생각합니다. metadatafactory는 id 전략을 결정할 때 상속을 확인합니다.
Seth Battin

실제로 루트 엔티티에만 추가하면 완벽하게 작동합니다. 둘 다에 추가하면 SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails오류 가 발생합니다. 비추천
ioleo 2014-09-13

5

새 솔루션은 모든 엔티티가 삽입 전에 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);

그러나 아직 다른 DBMS를 시도하지 않았기 때문에 MySQL에 대해서만 확인할 수 있습니다.


1

Doctrine 엔티티에 대한 향후 ID를 설정 하는 라이브러리 를 만들었습니다 . 영향을 최소화하기 위해 대기중인 모든 ID가 소비되면 원래 ID 생성 전략으로 되돌아갑니다. 이와 같은 코드를 반복 할 필요가 없도록 단위 테스트를위한 쉬운 드롭 인이어야합니다.


1

Villermen 작업에서 영감을 받아 , 엔티티가 AUTO, SEQUENCE, IDENTITY 또는 UUID 상태를 사용하는 경우에도 Doctrine 엔티티에 ID를 수동으로 할당 할 수 있는 라이브러리 tseho / doctrine-assigned-identity 를 만들었습니다 .

당신은 생산에 사용해서는 안 하지만, 기능 테스트 정말 유용합니다.

라이브러리는 할당 된 ID가있는 엔티티를 자동으로 감지하고 필요한 경우에만 생성기를 교체합니다. 인스턴스에 할당 된 ID가없는 경우 라이브러리는 초기 생성기를 대체합니다.

생성기 교체는 Doctrine EventListener에서 발생하므로 조명기에 추가 코드를 추가 할 필요가 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.