นี่คือวิธีที่ฉันแก้ไขหลักคำสอน"The EntityManager is closed" ปัญหา. โดยทั่วไปทุกครั้งที่มีข้อยกเว้น (เช่นคีย์ซ้ำ) หรือไม่ให้ข้อมูลสำหรับคอลัมน์บังคับจะทำให้ Doctrine ปิดตัวจัดการเอนทิตี หากคุณยังคงต้องการที่จะติดต่อกับฐานข้อมูลที่คุณต้องรีเซ็ตกิจการ Manger โดยการเรียกresetManager()
วิธีการดังกล่าวโดยJGrinon
ในแอปพลิเคชันของฉันฉันใช้งานผู้บริโภค RabbitMQ หลายรายที่ทำสิ่งเดียวกันทั้งหมด: ตรวจสอบว่ามีเอนทิตีอยู่ในฐานข้อมูลหรือไม่ถ้าใช่ส่งคืนหากไม่สร้างแล้วส่งคืน ในช่วงเวลาไม่กี่มิลลิวินาทีระหว่างการตรวจสอบว่าเอนทิตีนั้นมีอยู่แล้วหรือไม่และสร้างขึ้นโดยผู้บริโภครายอื่นเกิดขึ้นเพื่อทำแบบเดียวกันและสร้างเอนทิตีที่ขาดหายไปทำให้ผู้บริโภครายอื่นเกิดข้อยกเว้นคีย์ที่ซ้ำกัน ( เงื่อนไขการแข่งขัน )
สิ่งนี้นำไปสู่ปัญหาการออกแบบซอฟต์แวร์ โดยพื้นฐานแล้วสิ่งที่ฉันพยายามทำคือการสร้างเอนทิตีทั้งหมดในธุรกรรมเดียว สิ่งนี้อาจรู้สึกเป็นธรรมชาติสำหรับคนส่วนใหญ่ แต่ในกรณีของฉันผิดแนวคิดอย่างแน่นอน พิจารณาปัญหาต่อไปนี้: ฉันต้องจัดเก็บเอนทิตีการแข่งขันฟุตบอลที่มีการอ้างอิงเหล่านี้
- กลุ่ม (เช่นกลุ่ม A กลุ่ม B ... )
- รอบ (เช่นรอบรองชนะเลิศ ... )
- สถานที่จัดงาน (เช่นสนามกีฬาที่มีการแข่งขัน)
- สถานะการแข่งขัน (เช่นครึ่งเวลาเต็มเวลา)
- ทั้งสองทีมเล่นการแข่งขัน
- การแข่งขันนั้นเอง
ตอนนี้ทำไมการสร้างสถานที่ควรเป็นธุรกรรมเดียวกันกับการแข่งขัน? อาจเป็นไปได้ว่าฉันเพิ่งได้รับสถานที่ใหม่ซึ่งไม่ได้อยู่ในฐานข้อมูลของฉันดังนั้นฉันจึงต้องสร้างขึ้นก่อน แต่อาจเป็นไปได้ว่าสถานที่นั้นอาจเป็นเจ้าภาพจัดการแข่งขันอีกครั้งดังนั้นผู้บริโภครายอื่นอาจพยายามสร้างมันขึ้นมาด้วยในเวลาเดียวกัน ดังนั้นสิ่งที่ฉันต้องทำคือสร้างการอ้างอิงทั้งหมดก่อนในธุรกรรมแยกต่างหากเพื่อให้แน่ใจว่าฉันได้รีเซ็ตตัวจัดการเอนทิตีในข้อยกเว้นคีย์ที่ซ้ำกัน ฉันขอบอกว่าเอนทิตีทั้งหมดที่อยู่ในนั้นข้างการจับคู่สามารถกำหนดเป็น "แชร์" ได้เนื่องจากอาจเป็นส่วนหนึ่งของธุรกรรมอื่นในผู้บริโภครายอื่น สิ่งที่ไม่ "แชร์" ในนั้นคือการจับคู่ที่ไม่น่าจะสร้างขึ้นโดยผู้บริโภคสองคนในเวลาเดียวกัน
ทั้งหมดนี้ยังนำไปสู่อีกประเด็นหนึ่ง หากคุณรีเซ็ตตัวจัดการเอนทิตีอ็อบเจ็กต์ทั้งหมดที่คุณได้รับก่อนที่จะรีเซ็ตจะเป็นของ Doctrine ใหม่ทั้งหมด ดังนั้น Doctrine จะไม่พยายามเรียกใช้UPDATEกับพวกเขา แต่INSERT ! ดังนั้นตรวจสอบให้แน่ใจว่าคุณได้สร้างการอ้างอิงทั้งหมดของคุณในธุรกรรมที่ถูกต้องตามเหตุผลจากนั้นดึงอ็อบเจ็กต์ทั้งหมดของคุณกลับจากฐานข้อมูลก่อนตั้งค่าเป็นเอนทิตีเป้าหมาย พิจารณาโค้ดต่อไปนี้เป็นตัวอย่าง:
$group = $this->createGroupIfDoesNotExist($groupData);
$match->setGroup($group);
$venue = $this->createVenueIfDoesNotExist($venueData);
$round = $this->createRoundIfDoesNotExist($roundData);
นี่คือวิธีที่ฉันคิดว่าควรทำ
$group = $this->createGroupIfDoesNotExist($groupData);
$venue = $this->createVenueIfDoesNotExist($venueData);
$round = $this->createRoundIfDoesNotExist($roundData);
$group = $this->getGroup($groupData);
$venue = $this->getVenue($venueData);
$round = $this->getGroup($roundData);
$match->setGroup($group);
$match->setVenue($venue);
$match->setRound($round);
$matchTeamHome = new MatchTeam();
$matchTeamHome->setMatch($match);
$matchTeamHome->setTeam($teamHome);
$matchTeamAway = new MatchTeam();
$matchTeamAway->setMatch($match);
$matchTeamAway->setTeam($teamAway);
$match->addMatchTeam($matchTeamHome);
$match->addMatchTeam($matchTeamAway);
$em->persist($match);
$em->persist($matchTeamHome);
$em->persist($matchTeamAway);
$em->flush();
ฉันหวังว่ามันจะช่วย :)