Symfony4+Doctrine中ManyToOne关联导致Filesupload重复创建求助
Hey there, let's break down why you're seeing duplicate Filesupload records and how to fix it without relying on cascade={"persist"}.
Why This Happens
The root issue is that the Filesupload object you're associating with ESA entries isn't staying in the managed state of Doctrine's EntityManager between batch flushes. Unlike your Department records (which are pre-existing and already managed), your Filesupload is a newly created object—and if it gets detached (e.g., after calling clear()), Doctrine will treat it as a new entity and persist it again.
Your attempts with merge() or fetching by ID might have failed because you weren't re-establishing the managed state correctly after batch operations.
Solutions to Try
1. Reuse the Original Managed Filesupload Object (With Clear() Safeguard)
If you want to stick with the initial Filesupload object, just make sure you re-fetch it as a managed entity after calling clear() (since clear() detaches all objects from the EntityManager):
// Step 1: Create and persist your Filesupload first $fileUpload = new Filesupload(); $fileUpload->setFileName($csvFileName); // Set other Filesupload properties... $entityManager->persist($fileUpload); $entityManager->flush(); $fileUploadId = $fileUpload->getId(); // Store the ID for later // Step 2: Batch process CSV rows $batchSize = 80; $recordCount = 0; foreach ($csvRows as $row) { $esa = new Esa(); // Populate ESA fields from CSV row... // Use the managed Filesupload object $esa->setFilesupload($fileUpload); $esa->setDepartment($existingManagedDepartment); $entityManager->persist($esa); $recordCount++; if ($recordCount % $batchSize === 0) { $entityManager->flush(); $entityManager->clear(); // This detaches ALL objects, including $fileUpload // Re-fetch Filesupload as a managed entity $fileUpload = $entityManager->getRepository(Filesupload::class)->find($fileUploadId); } } // Flush remaining records if ($recordCount % $batchSize !== 0) { $entityManager->flush(); }
2. Use Doctrine's getReference() for Lightweight Managed Proxies
If you want to avoid extra database queries, use EntityManager::getReference()—it returns a managed proxy object for your Filesupload without hitting the database (until you access its properties). This works perfectly for associations:
// Step 1: Create and persist Filesupload, grab its ID $fileUpload = new Filesupload(); // Set properties... $entityManager->persist($fileUpload); $entityManager->flush(); $fileUploadId = $fileUpload->getId(); // Step 2: Batch import ESA records $batchSize = 80; $recordCount = 0; foreach ($csvRows as $row) { $esa = new Esa(); // Populate ESA fields... // Use a managed proxy for Filesupload $esa->setFilesupload($entityManager->getReference(Filesupload::class, $fileUploadId)); $esa->setDepartment($existingManagedDepartment); $entityManager->persist($esa); $recordCount++; if ($recordCount % $batchSize === 0) { $entityManager->flush(); $entityManager->clear(); // Proxy will be recreated as managed on next use } } if ($recordCount % $batchSize !== 0) { $entityManager->flush(); }
3. Double-Check for Accidental Object Instantiation
Make sure you're not accidentally creating a new Filesupload instance inside your CSV loop. It's easy to slip up and write new Filesupload() somewhere in the loop, which would trigger duplicate persists.
Key Takeaway
Doctrine only persists entities that are in its managed state. As long as you're associating ESA records with a managed Filesupload object (either the original fetched instance or a proxy from getReference()), it won't create duplicate records.
内容的提问来源于stack exchange,提问作者Reverse_side_

