ตั้งค่า Id ด้วยหลักคำสอนอย่างชัดเจนเมื่อใช้กลยุทธ์ "อัตโนมัติ"


101

หน่วยงานของฉันใช้คำอธิบายประกอบนี้เพื่อเป็น 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());

โซลูชันเก่า

เนื่องจากหลักคำสอนหมุนออกจาก 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

2
เอริคไม่เป็นไร ... ฉันเห็นสิ่งที่คุณพยายามทำ โดยพื้นฐานแล้วคุณต้องมี @GeneratedValue (strategy = "ItDepends") :)
Deyon Samuel Washington

1
สิ่งหนึ่งที่ควรทราบเกี่ยวกับเรื่องนี้คือดูเหมือนว่าตัวสร้าง Id ที่ไม่ใช่ "isPostInsertGenerator" == true จะทำงานแล้ว คุณสามารถเปลี่ยนค่าของ ID ได้หลังจากที่ยังคงมีอยู่อย่างไรก็ตามคุณจะสูญเสียหมายเลขลำดับ
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

คำตอบ:


52

แม้ว่าโซลูชันของคุณจะทำงานได้ดีกับ MySQL แต่ฉันก็ล้มเหลวในการทำให้มันทำงานกับ PostgreSQL ได้เนื่องจากเป็นแบบลำดับ

ฉันได้เพิ่มบรรทัดนี้เพื่อให้ทำงานได้อย่างสมบูรณ์:

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

ขอแสดงความนับถืออย่างสูง,


ขอบคุณ! หลักคำสอนดีขึ้นเล็กน้อยตั้งแต่นี่เป็นปัญหาแรกฉันจึงยอมรับคำตอบของคุณและอัปเดตตั๋วต้นฉบับของฉันตามนั้น
Eric

ขอบคุณและยินดีที่จะช่วยเล็กน้อยเท่าที่จะทำได้ :)
nicolasbui

3
จะตั้งเครื่องกำเนิดไฟฟ้านี้ถาวรหรือไม่ ฉันสามารถเพิ่มหนึ่งระเบียนด้วยรหัสบังคับแล้วปล่อยให้ใช้รหัสการเพิ่มอัตโนมัติได้หรือไม่
Pavel Dubinin

1
ฉันยืนยันได้ว่าสิ่งนี้ใช้ได้กับ Symfony 3.2 สิ่งที่ฉันไม่ได้คาดหวัง แต่เป็นที่เครื่องกำเนิดไฟฟ้าจะต้องมีการตั้งค่าหลังจาก$em->persist($entity)การดำเนินการ
bodo

30

บางทีหลักคำสอนที่เปลี่ยนไป แต่ตอนนี้วิธีที่ถูกต้องคือ:

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

1
นี่ยังคงเป็นข้อมูลที่เกี่ยวข้องและใช้ได้กับหลักคำสอน 2.4.1 แต่ควรลบบรรทัดที่สองตามที่ @gphilip กล่าวไว้
Mantas

ใช้ไม่ได้กับหลักคำสอน> 2.5 เนื่องจากClassMetadataเป็นส่วนต่อประสานและด้วยเหตุนี้จึงไม่มีค่าคงที่
TiMESPLiNTER

มีคลาสClassMetadata
Alexey B.

@gphilip บรรทัดที่สองเป็นสิ่งสำคัญหากคุณต้องการที่จะทำงานร่วมกับสมาคม
Taz

1
สามารถทำให้ง่ายขึ้นโดยใช้$metadata::GENERATOR_TYPE_NONE
Will B.

7

ในกรณีที่เอนทิตีเป็นส่วนหนึ่งของการสืบทอดตารางคลาสคุณต้องเปลี่ยนตัวสร้าง id ในข้อมูลเมตาของคลาสสำหรับทั้งสองเอนทิตี (เอนทิตีที่คุณยังคงอยู่และเอนทิตีหลัก)


ฉันคิดว่าในกรณีนี้คือคุณต้องระบุเอนทิตีรากเท่านั้น metadatafactory ตรวจสอบการสืบทอดเมื่อกำหนดกลยุทธ์ id
Seth Battin

ในความเป็นจริงเมื่อฉันเพิ่มลงในเอนทิตีรูทเท่านั้นมันทำงานได้อย่างไม่มีที่ติ เมื่อฉันเพิ่มทั้งสองอย่างฉันได้รับSQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint failsข้อผิดพลาด Downvoted
ioleo

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

อย่างไรก็ตามฉันสามารถยืนยันได้เฉพาะกับ MySQL เท่านั้นเนื่องจากฉันยังไม่ได้ลอง DBMS อื่น


1

ฉันได้สร้างห้องสมุดเพื่อตั้งรหัสในอนาคตสำหรับเอนทิตีหลักคำสอน จะเปลี่ยนกลับไปใช้กลยุทธ์การสร้าง ID ดั้งเดิมเมื่อ ID ที่อยู่ในคิวทั้งหมดถูกใช้เพื่อลดผลกระทบ ควรเป็นดรอปอินที่ง่ายสำหรับการทดสอบหน่วยเพื่อไม่ให้โค้ดแบบนี้ต้องทำซ้ำ


1

ด้วยแรงบันดาลใจจากงานของVillermenฉันได้สร้างตัวตนที่ได้รับมอบหมายจากไลบรารี/ หลักคำสอนซึ่งช่วยให้คุณสามารถกำหนด ID ให้กับเอนทิตีหลักคำสอนได้ด้วยตนเองแม้ว่าเอนทิตีจะใช้หมวดหมู่ AUTO, SEQUENCE, IDENTITY หรือ UUID ก็ตาม

คุณไม่ควรใช้ในการผลิตแต่มีประโยชน์มากสำหรับการทดสอบการทำงาน

ไลบรารีจะตรวจจับเอนทิตีโดยอัตโนมัติด้วย id ที่กำหนดและแทนที่ตัวสร้างเมื่อจำเป็นเท่านั้น ไลบรารีจะสำรองบนตัวสร้างเริ่มต้นเมื่ออินสแตนซ์ไม่มี id ที่กำหนด

การเปลี่ยนเครื่องกำเนิดไฟฟ้าเกิดขึ้นใน Doctrine EventListener โดยไม่จำเป็นต้องเพิ่มรหัสเพิ่มเติมในส่วนควบของคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.