【问题标题】:How to disable cascade deletion for many-to-many relations?如何禁用多对多关系的级联删除?
【发布时间】:2015-04-20 00:39:13
【问题描述】:

对于多对多关系(例如组和用户),一旦删除任何一个实体,联合表中的行就会自动删除,就像为关系设置了级联删除属性一样。所以基本上我想删除它如果它是空的。所以解决方案必须保证没有关系被丢弃(就像 FK 约束保证它一样)。

是否可以默认不这样做并在违反外键约束时抛出异常?

PS:删除前检查不是解决方案,因为它很容易出现竞争条件。

PPS:映射定义是微不足道的,为了完整起见,我将它们发布在这里(即使它们没有带来任何有用的东西)

PPPS:onDelete: cascade 也不是解决方案:它在数据库级别创建相应的ON DELETE CASCADE

PPPPS:ON DELETE RESTRICT 不能使用,因为学说将从联合表中删除所有引用。

在角色中:

manyToMany:
    users:
        targetEntity: UserAccount
        mappedBy: roles

在用户中:

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

【问题讨论】:

  • 我认为数据库级别的ON DELETE RESTRICT 也不是一个选项?
  • 选择什么?我不想从联合表中删除任何内容。
  • 也许我不理解最初的问题,但是如果您想在尝试删除外键引用的行时抛出异常,您可以在 DB 中设置 ON DELETE RESTRICT。然后这会简单地从 DB 中抛出一个异常。
  • @awons 原则会从联合表中一一删除行,因此永远不会使用 ON DELETE RESTRICT
  • Doctrine 默认没有一对多的级联删除。所以你可以使用第三类,比如Definition,默认不会被删除。

标签: php database symfony doctrine-orm doctrine


【解决方案1】:

这个答案可以被视为一种解决方法many-to-many 关联可以替换为 3 个参与类之间的 one-to-many/many-to-one 关联,因为默认情况下,Doctrine 没有与 one-to-many 的级联删除。

【讨论】:

    【解决方案2】:

    几个小时前我遇到了同样的问题: 我在UserGroup 实体之间有一个简单的ManyToMany 关联。

    如果有一些相关的users,我不希望Doctrine 删除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(),并且您会获得异常。

    【讨论】:

      猜你喜欢
      • 2013-08-21
      • 1970-01-01
      • 1970-01-01
      • 2016-04-11
      • 1970-01-01
      • 2016-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多