Đây là cách tôi giải quyết Doctrine "EntityManager bị đóng." vấn đề. Về cơ bản, mỗi lần có một ngoại lệ (tức là khóa trùng lặp) hoặc không cung cấp dữ liệu cho một cột bắt buộc sẽ khiến Doctrine đóng Trình quản lý thực thể. Nếu bạn vẫn muốn tương tác với cơ sở dữ liệu, bạn phải đặt lại Trình quản lý thực thể bằng cách gọi resetManager()
phương thức như được đề cập bởi JGrinon .
Trong ứng dụng của mình, tôi đang chạy nhiều người tiêu dùng RabbitMQ đều làm cùng một việc: kiểm tra xem một thực thể có ở đó trong cơ sở dữ liệu hay không, nếu có, hãy trả lại nó, nếu không hãy tạo nó và sau đó trả lại nó. Trong vài phần nghìn giây giữa việc kiểm tra xem thực thể đó đã tồn tại hay chưa và tạo ra nó, một người tiêu dùng khác đã tình cờ làm như vậy và tạo ra thực thể bị thiếu khiến người tiêu dùng khác phải chịu một ngoại lệ khóa trùng lặp ( điều kiện chủng tộc ).
Điều này dẫn đến một vấn đề thiết kế phần mềm. Về cơ bản những gì tôi đang cố gắng làm là tạo tất cả các thực thể trong một giao dịch. Điều này có thể cảm thấy tự nhiên với hầu hết nhưng chắc chắn là sai về mặt khái niệm trong trường hợp của tôi. Hãy xem xét vấn đề sau: Tôi đã phải lưu trữ một thực thể Trận đấu bóng đá có những phụ thuộc này.
- một nhóm (ví dụ: Nhóm A, Nhóm B ...)
- một vòng (ví dụ như Bán kết ...)
- một địa điểm (tức là sân vận động nơi trận đấu đang diễn ra)
- trạng thái trận đấu (ví dụ: nửa thời gian, toàn thời gian)
- hai đội chơi trận đấu
- trận đấu chính nó
Bây giờ, tại sao việc tạo địa điểm phải được thực hiện trong cùng một giao dịch với trận đấu? Có thể là tôi vừa nhận được một địa điểm mới mà nó không có trong cơ sở dữ liệu của tôi nên tôi phải tạo nó trước. Nhưng nó cũng có thể là địa điểm đó có thể tổ chức một trận đấu khác nên một người tiêu dùng khác có thể sẽ cố gắng tạo ra nó cùng lúc. Vì vậy, những gì tôi phải làm là tạo tất cả các phụ thuộc trước trong các giao dịch riêng biệt để đảm bảo rằng tôi đang đặt lại trình quản lý thực thể trong một ngoại lệ khóa trùng lặp. Tôi muốn nói rằng tất cả các thực thể trong đó bên cạnh trận đấu có thể được định nghĩa là "được chia sẻ" vì chúng có thể là một phần của các giao dịch khác ở những người tiêu dùng khác. Thứ gì đó không được "chia sẻ" trong đó là bản thân kết quả trùng khớp sẽ không có khả năng được tạo bởi hai người tiêu dùng cùng một lúc.
Tất cả điều này cũng dẫn đến một vấn đề khác. Nếu bạn đặt lại Trình quản lý thực thể, tất cả các đối tượng bạn đã truy xuất trước khi đặt lại dành cho Doctrine hoàn toàn mới. Vì vậy, Doctrine sẽ không cố chạy CẬP NHẬT trên chúng mà là CHÈN ! Vì vậy, hãy đảm bảo rằng bạn tạo tất cả các phụ thuộc của mình trong các giao dịch chính xác một cách hợp lý và sau đó lấy lại tất cả các đối tượng của bạn từ cơ sở dữ liệu trước khi đặt chúng thành thực thể đích. Hãy coi đoạn mã sau làm ví dụ:
$group = $this->createGroupIfDoesNotExist($groupData);
$match->setGroup($group);
$venue = $this->createVenueIfDoesNotExist($venueData);
$round = $this->createRoundIfDoesNotExist($roundData);
Vì vậy, đây là cách tôi nghĩ nó nên được thực hiện.
$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();
Tôi hy vọng nó sẽ giúp :)