如何禁用多对多关系的级联删除?
How to disable cascade deletion for many-to-many relations?
对于多对多关系(例如组和用户),只要删除其中一个实体,联合 table 中的行就会自动删除,就像为 cascade remove 属性设置一样的关系。所以基本上我想删除它 IF ONLY IT IS EMPTY。所以解决方案必须保证没有关系被删除(就像 FK 约束保证它一样)。
是否可以默认不这样做并在违反外键约束时抛出异常?
PS:删除前检查不是解决方案,因为它容易出现竞争条件。
PPS: 映射定义很琐碎,为了完整起见,我在这里 post 它们(即使它们没有带来任何有用的东西)
PPPS: onDelete: cascade
也不是解决方案:它在数据库级别创建相应的 ON DELETE CASCADE
。
PPPPS: ON DELETE RESTRICT
不能使用 因为学说将从联合 table.[=15= 中删除所有引用]
担任角色:
manyToMany:
users:
targetEntity: UserAccount
mappedBy: roles
在用户中:
manyToMany:
roles:
targetEntity: Role
joinTable:
name: user_role
joinColumns:
user_id:
referencedColumnName: id
inverseJoinColumns:
role_id:
referencedColumnName: id
这个答案可以被视为解决方法。 many-to-many
关联可以替换为3个参与类之间的one-to-many/many-to-one
关联,因为Doctrine默认没有与one-to-many
的级联删除。
几个小时前我遇到了同样的问题:
我在 User
和 Group
实体之间有一个简单的 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 class
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');
}
也许最好在 preFlush
或 onFlush
事件上创建一个侦听器,因为您实际上可以在对象上调用 remove()
而无需刷新,并且您会获得异常。
对于多对多关系(例如组和用户),只要删除其中一个实体,联合 table 中的行就会自动删除,就像为 cascade remove 属性设置一样的关系。所以基本上我想删除它 IF ONLY IT IS EMPTY。所以解决方案必须保证没有关系被删除(就像 FK 约束保证它一样)。
是否可以默认不这样做并在违反外键约束时抛出异常?
PS:删除前检查不是解决方案,因为它容易出现竞争条件。
PPS: 映射定义很琐碎,为了完整起见,我在这里 post 它们(即使它们没有带来任何有用的东西)
PPPS: onDelete: cascade
也不是解决方案:它在数据库级别创建相应的 ON DELETE CASCADE
。
PPPPS: ON DELETE RESTRICT
不能使用 因为学说将从联合 table.[=15= 中删除所有引用]
担任角色:
manyToMany:
users:
targetEntity: UserAccount
mappedBy: roles
在用户中:
manyToMany:
roles:
targetEntity: Role
joinTable:
name: user_role
joinColumns:
user_id:
referencedColumnName: id
inverseJoinColumns:
role_id:
referencedColumnName: id
这个答案可以被视为解决方法。 many-to-many
关联可以替换为3个参与类之间的one-to-many/many-to-one
关联,因为Doctrine默认没有与one-to-many
的级联删除。
几个小时前我遇到了同样的问题:
我在 User
和 Group
实体之间有一个简单的 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 class
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');
}
也许最好在 preFlush
或 onFlush
事件上创建一个侦听器,因为您实际上可以在对象上调用 remove()
而无需刷新,并且您会获得异常。