簡體   English   中英

如何禁用多對多關系的級聯刪除?

[英]How to disable cascade deletion for many-to-many relations?

對於多對多關系(例如組和用戶),一旦刪除任何一個實體,聯合表中的行就會自動刪除,就像為關系設置了級聯刪除屬性一樣。 所以基本上我想刪除它IF ONLY IT IS EMPTY 所以解決方案必須保證沒有關系被丟棄(就像 FK 約束保證它一樣)。

是否可以默認不這樣做並在違反外鍵約束時拋出異常?

PS:刪除前檢查不是解決方案,因為它很容易出現競爭條件。

PPS:映射定義是微不足道的,為了完整起見,我將它們發布在這里(即使它們沒有帶來任何有用的東西)

PPPS: onDelete: cascade也不是解決方案:它在數據庫級別創建相應的ON DELETE CASCADE

PPPPS:不能使用ON DELETE RESTRICT ,因為 doctrine 將從聯合表中刪除所有引用。

在角色中:

manyToMany:
    users:
        targetEntity: UserAccount
        mappedBy: roles

在用戶中:

manyToMany:
    roles:
        targetEntity: Role
        joinTable:
            name: user_role
            joinColumns:
                user_id:
                    referencedColumnName: id
            inverseJoinColumns:
                role_id:
                    referencedColumnName: id

此答案可以視為一種解決方法 3個參與課程之間的many-to-many關聯可以替換one-to-many/many-to-one關聯,因為默認情況下,Doctrine沒有使用one-to-many的級聯刪除功能。

一對多/多對一

幾個小時前,我遇到了同樣的問題: UserGroup實體之間有一個簡單的ManyToMany關聯。

如果有一些相關的users ,我不希望Doctrine刪除一個Group實體,而即使在某個Group中,我也不希望Doctrine刪除一個User

為此,在Group實體中,我具有以下代碼行:

/**
 * Group
 *
 * @ORM\Entity()
 * @ORM\HasLifecycleCallbacks()
 * 
 */

class Group
{
...
    /**
     * @var \Doctrine\Common\Collections\ArrayCollection
     * 
     * @ORM\ManyToMany(targetEntity="User", mappedBy="groups")
     * 
     */
    private $users;

...

    /**
     * @ORM\PreRemove()
     */
    public function preRemove() {
    if ($this->users->count()>0) {
        throw new \AppBundle\Utils\Exception\FKConstraintViolationException();
    }
    }

}

這是FKConstraintViolationException類

namespace AppBundle\Utils\Exception;

class FKConstraintViolationException extends \Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException {
    public function __construct() {
            $message='Self generated exception';
            $driverException=new \Doctrine\DBAL\Driver\OCI8\OCI8Exception('Self generated exception.'); //or whatever driver you use if you have another db
            parent::__construct($message, $driverException);
    }
}

然后在控制器中,將remove()放在try/catch塊中

try {
    $em->remove($group);
    $em->flush();
    $this->addFlash('success', 'deleted_successfully');
} catch (\Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException $e) {
    $this->addFlash('error', 'cannotdelete_related_entities');
}

這是文檔http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#lifecycle-events

也許最好在preFlushonFlush事件上創建偵聽器,因為您實際上可以在不刷新的情況下在對象上調用remove()並獲得異常。

在遇到同樣的問題並沒有找到任何解決方案后,我花了幾個小時試圖找出發生這種情況的原因。 我在這里為遇到此問題的其他人發布我的發現。

簡而言之

  • 確保Role是關系的擁有
  • onDelete: 'restrict'添加到 JoinColumns。 (例如,如果您使用的是 symfony 制造商,這將導致使用正確的 ON DELETE RESTRICT 進行遷移)
  • onDelete: 'cascade'添加到 InverseJoinColumns。 (這將防止數據被刪除)

以下是使用屬性的示例 class 實現:

#[ORM\Entity]
class User
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private $id;

    #[ORM\ManyToMany(targetEntity: Role::class, mappedBy: 'users')]
    private $roles;
}

#[ORM\Entity]
class Role
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private $id;

    #[ORM\ManyToMany(targetEntity: User::class, inversedBy: 'roles')]
    #[ORM\JoinColumn(onDelete: 'restrict')]
    #[ORM\InverseJoinColumn(onDelete: 'cascade')]
    private $users;
}

更詳細

現在我不確定它是否應該這樣工作,但以下發現使我找到了這個解決方案:

出錯的地方在於方法Doctrine\ORM\Persisters\Entity\BasicEntityPersister::deleteJoinTableRecords()

具體情況

if (isset($mapping['isOnDeleteCascade'])) {
    continue;
}

基本上,除非設置了此映射,否則 doctrine 將自動添加一個查詢以刪除關系表中的所有現有條目。

現在,實際設置此映射鍵的位置在方法Doctrine\ORM\Mapping\ClassMetadataInfo::_validateAndCompleteManyToManyMapping()

這是我發現的唯一一個將isOnDeleteCascade添加到映射的地方。 此方法同時檢查 JoinColumn 和 InverseJoinColumn,如果其中任何一個具有onDelete='cascade' ,則該鍵設置為 true。 具體來說:

if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) === 'cascade') {
    $mapping['isOnDeleteCascade'] = true;
}

不幸的是,我在文檔中找不到有關該問題的太多信息,或者這是否是處理此問題的預期方法。 從我所見,不可能像目前在 Doctrine 中實現的方式那樣對兩個外鍵都有 RESTRICT 條件。 但是當只有一方需要限制另一方時,它會起作用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM