Đặt Id rõ ràng với Doctrine khi sử dụng chiến lược "TỰ ĐỘNG"


100

Tổ chức của tôi sử dụng chú thích này cho ID của nó:

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

Từ cơ sở dữ liệu sạch, tôi đang nhập các bản ghi hiện có từ cơ sở dữ liệu cũ hơn và cố gắng giữ các ID giống nhau. Sau đó, khi thêm các bản ghi mới, tôi muốn MySQL tự động tăng cột ID như bình thường.

Thật không may, có vẻ như Doctrine2 hoàn toàn bỏ qua ID được chỉ định.


Giải pháp mới

Theo các khuyến nghị dưới đây, giải pháp sau là giải pháp ưu tiên:

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

Giải pháp cũ

Vì Doctrine xoay quanh ClassMetaData để xác định chiến lược trình tạo, nó phải được sửa đổi sau khi quản lý thực thể trong EntityManager:

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

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

$this->em->flush();

Tôi vừa thử nghiệm điều này trên MySQL và nó hoạt động như mong đợi, có nghĩa là Các thực thể có ID tùy chỉnh được lưu trữ với ID đó, trong khi những thực thể không có ID được chỉ định sử dụng lastGeneratedId() + 1.


Bạn có đang sử dụng học thuyết để nhập các bản ghi hiện có không?
rojoca

2
Eric, đừng bận tâm ... Tôi hiểu những gì bạn đang cố gắng làm. Về cơ bản, bạn cần một @GeneratedValue (chiến lược = "ItDepends") :)
Wil Moore III

1
Một điều cần lưu ý về điều này, là có vẻ như các trình tạo Id không phải là "isPostInsertGenerator" == true, sẽ đã chạy. Bạn có thể thay đổi giá trị của ID sau khi tiếp tục, tuy nhiên, bạn sẽ mất một số thứ tự.
gview

15
Giải pháp mới hiện cho phép tôi đặt id trong một bộ cố định học thuyết. Tuy nhiên, sử dụng $ metadata-> setIdGeneratorType (\ Doctrine \ ORM \ Mapping \ ClassMetadata :: GENERATOR_TYPE_NONE); cho phép đặt và lưu id. (MySQL).
jmoz

2
Giải pháp mới đó không hoạt động trong Symfony 3.0. Tôi đã phải sử dụng$metadata = $this->getEntityManager()->getClassMetaData(User::class); $metadata->setIdGenerator(new AssignedGenerator()); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);
piotrekkr

Câu trả lời:


51

Mặc dù giải pháp của bạn hoạt động tốt với MySQL, nhưng tôi đã không thể làm cho nó hoạt động với PostgreSQL vì nó dựa trên trình tự.

Tôi đã thêm dòng này để làm cho nó hoạt động hoàn hảo:

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

Trân trọng,


Cảm ơn! Doctrine đã được cải thiện một chút vì đây là sự cố đầu tiên, vì vậy tôi đã chấp nhận câu trả lời của bạn và cập nhật vé ban đầu của tôi cho phù hợp.
Eric

Cảm ơn bạn và tôi rất vui được giúp đỡ một chút khi tôi có thể :)
nicolasbui

2
điều này sẽ đặt máy phát điện này vĩnh viễn? Tôi có thể thêm một bản ghi với ID bắt buộc và sau đó để nó sử dụng id tự động gia tăng không?
Pavel Dubinin

1
Tôi có thể xác nhận rằng điều này hoạt động với Symfony 3.2. Tuy nhiên, điều tôi không mong đợi là bộ tạo phải được đặt sau khi thực thi $em->persist($entity).
bodo

29

Có lẽ điều mà học thuyết đã thay đổi nhưng bây giờ đúng là:

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

1
Đây vẫn là thông tin có liên quan và hoạt động cho Doctrine 2.4.1, nhưng dòng thứ hai như được đề cập bởi @gphilip nên bị xóa.
Mantas

Không hoạt động đối với Doctrine> 2.5 vì ClassMetadatalà một giao diện và do đó không thể có bất kỳ hằng số nào.
TiMESPLiNTER

Có một lớp ClassMetadata
Alexey B.

@gphilip Dòng thứ hai rất quan trọng nếu bạn muốn nó hoạt động với các liên kết .
Taz

1
Có thể đơn giản hóa bằng cách sử dụng$metadata::GENERATOR_TYPE_NONE
fyrye

7

Trong trường hợp thực thể là một phần của kế thừa bảng lớp, bạn cần thay đổi trình tạo id trong siêu dữ liệu lớp cho cả hai thực thể (thực thể bạn đang duy trì và thực thể gốc)


Tôi nghĩ rằng trường hợp là bạn chỉ cần chỉ định thực thể gốc. Siêu dữ liệu kiểm tra tính kế thừa khi xác định chiến lược id.
Seth Battin,

Trên thực tế, khi tôi chỉ thêm nó vào thực thể gốc, nó hoạt động hoàn hảo. Khi tôi thêm nó vào cả hai, tôi sẽ gặp SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint failslỗi. Downvoted
ioleo

5

Giải pháp mới chỉ hoạt động tốt khi TẤT CẢ các thực thể có id trước khi chèn. Khi một thực thể có ID và một thực thể khác thì không - giải pháp mới không thành công.

Tôi sử dụng chức năng này để nhập tất cả dữ liệu của mình:

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

Giải pháp cho Doctrine 2.5 và MySQL

"Giải pháp mới" không hoạt động với Doctrine 2.5 và MySQL. Bạn phải sử dụng:

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

Tuy nhiên, tôi chỉ có thể xác nhận điều đó cho MySQL, vì tôi chưa thử bất kỳ DBMS nào khác.


1

Tôi đã tạo một thư viện để đặt ID trong tương lai cho các thực thể Doctrine. Nó hoàn nguyên về chiến lược tạo ID ban đầu khi tất cả các ID được xếp hàng đợi được sử dụng để giảm thiểu tác động. Nó phải là một phần dễ dàng cho các bài kiểm tra đơn vị để mã như thế này không phải lặp lại.


1

Lấy cảm hứng từ tác phẩm của Villermen , tôi đã tạo thư viện tseho / doct-being -igned- ID cho phép bạn gán ID theo cách thủ công cho một thực thể Doctrine, ngay cả khi thực thể đó sử dụng trạng thái AUTO, SEQUENCE, IDENTITY hoặc UUID.

Bạn không bao giờ nên sử dụng nó trong sản xuất nhưng nó thực sự hữu ích cho các bài kiểm tra chức năng.

Thư viện sẽ tự động phát hiện các thực thể có id được chỉ định và chỉ thay thế trình tạo khi cần thiết. Thư viện sẽ dự phòng trên trình tạo ban đầu khi một cá thể không có id được chỉ định.

Việc thay thế trình tạo xảy ra trong Doctrine EventListener, không cần thêm bất kỳ mã bổ sung nào vào đồ đạc của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.