Doctrine2/Symfony2中的插入忽略重复条目

24

如何使用Doctrine2忽略重复条目?

错误示例:

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'symfony' for key 'UNIQ_389B783389B783'

你可以像 Propel 一样做:创建一个名为 findByKeyOrCreate() 的方法,如果存在实体则返回该实体,否则创建一个新的实体。 - gremo
如果你不需要的话,可以从该字段中删除 @Unique 索引。 - Anton Babenko
当尝试保存非唯一条目时,您期望从中获得什么结果? - Anton Babenko
5个回答

25

这是Doctrine的一个麻烦之处,它不支持INSERT/UPDATE Ignore,有一些解决方法,比如创建一个方法来检查行是否存在,如果存在,则跳过该行。

您可以捕获异常,以便您的脚本不会因异常而结束。但是,实体管理器将关闭,您将无法再使用它。您仍然可以使用PDO,并且可以在数据库中插入记录,指示批处理失败,因为X需要重新启动(这通常是我所做的)。

如果以上选项都不适用于您,最终我会编写原始的SQL来进行批处理,根本不使用Doctrine,这样做更快速,而可执行INSERT/UPDATE Ignore使其成为不言而喻的选择。


如果您不介意覆盖项目,可以使用合并作为 https://dev59.com/zGUo5IYBdhLWcg3wtRMx#15838232 中所述(已过时,并将在Doctrine ORM 3.0中删除)。 - M at

23

Symfony 3 中,如果捕获到 UniqueConstraintViolationException 异常,您可以通过调用 Doctrine 对象的 resetManager() 方法来重置管理器并继续使用它。

以下是一个示例:

try {
  $em = $this->getDoctrine()->getManager();
  $entity = Product::create()
    ->setTitle('Some title')
    ->setUrl('http://google.com');
  $em->persist($entity);
  $em->flush();
}
catch (UniqueConstraintViolationException $e) {
  $this->getDoctrine()->resetManager();
}

2
不错的分享!(例如resetManager()) - Marius S
4
我对那种类型的更新答案很感兴趣。resetManager()现在已经过时了。 - Moonchild
2
如果您正在使用Symfony 3/4,请使用以下命令:composer require symfony/proxy-manager-bridge - Karol May

13

您可以捕获异常然后忽略它。但请注意,当实体管理器引发异常时,在该请求期间将无法再使用实体管理器。


1
这是一个问题的答案。Symfony版本应该大于等于4.0,Doctrine版本应该大于等于2.0。
我的实体有两个唯一字段,分别是电话渠道
在您的实体类中导入此类即可。
use Doctrine\Persistence\NotifyPropertyChanged;
use Doctrine\Persistence\PropertyChangedListener;

实现NotifyPropertyChanged类后

class TblPersonPhonePermission implements NotifyPropertyChanged

在添加了addPropertyChangedListener方法之后。
public function addPropertyChangedListener(PropertyChangedListener $listener)
{
    $insertBatch = $listener->getScheduledEntityInsertions();
    foreach ($insertBatch as $entity) {
        if (
            (get_class($entity) === get_class($this)) &&
            $this->getId() !== $entity->getId() &&
            $this->getPhone() === $entity->getPhone() &&
            $this->getChannel() === $entity->getChannel()
        ) {
            $listener->remove($this);
        }
    }
}

0

在实际插入之前,您还可以检查重复项。我曾遇到类似的问题,并在此处给出了答案:doctrine/symfony 4 - avoid duplicate entry when persisting child entities

protected function removeDuplicates($insertions) {
    foreach ($insertions as $key => $insertion) {
        $shortClassName = (new ReflectionClass($insertion))->getShortName();
        // TODO: The search can be heavily optimized
        foreach ($insertions as $possibleDupeKey => $possibleDupeInsertion) {
            $shortDupeClassName = (new ReflectionClass($insertion))->getShortName();
            // TODO: Treat case when unique is on a field not called 'id'
            if ($shortClassName === $shortDupeClassName && $insertion->getId() === $possibleDupeInsertion->getId() && $key !== $possibleDupeKey) {
                $this->em->remove($possibleDupeInsertion);
            }
        }
    }
}

protected function saveStuff($order) {
    $this->em->persist($order);
    $this->removeDuplicates($this->em->getUnitOfWork()->getScheduledEntityInsertions());
    $this->em->flush();
}

3
只有在您将重复保存在同一线程中时才有用,这是更容易的情况。问题通常是两个线程(两个服务器请求)同时尝试创建相同的内容。 - amik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接