交易期间密钥 'PRIMARY' 的重复条目“...”

Duplicate entry '...' for key 'PRIMARY' during the transaction

这件事情是昨晚发生在我身上的。我非常熟悉错误的性质,但我仍然无法弄清楚是什么原因造成的。我可能有预感,但我不确定。我将从一些基本的应用信息开始:​​

我的应用有 3 个实体:LoanSystemPageTextPage。每当有人添加贷款时,一个或多个系统页面就会被添加到数据库中。基本上,它是这样的:

if ( $form->isValid()){
    $this->em->getConnection()->beginTransation();
    $this->em->persist($loan);
    $this->em->flush();

    while ($someCondition){
        $page = new SystemPage();
        //... Fill the necessary data into page
        $page->setObject($loan);
        $this->em->persist($page);
    }

    $this->em->flush();
    $this->em->getConnection()->commit();
}

请忽略潜在的错别字,我是根据字面意思写的

实体 Loan 映射到 table loansSystemPage 映射(通过继承映射)到 system_pagesbase_pages。后面两个都有 id 字段设置为 AUTO_INCREMENT.

我的直觉:还有一个 table 叫做 text_pages。鉴于一方面 text_pagesbase_pages 以及另一方面 system_pagesbase_pages 共享 ID,我认为它可以很容易地导致这个:

User1: Create BasePage, acquire autoincrement ID (value = 1)
User2: Create BasePage, acquire autoincrement ID (value = 1)
User1: Create TextPage, use the ID from step 1
User2: Create SystemPage, use the ID from step 2

这个理论的两个问题:

重要提示:稍等一分钟,重新提交成功。

这会不会是一些奇怪的 MySQL 事务隔离错误?任何提示将不胜感激...

编辑:

数据库模式的一部分:

请忽略塞尔维亚语的列名

flush() 操作在单个事务中刷新所有更改,因此此处有冗余代码...

你没有说你是否可以重现这个错误,如果你能提供数据库模式会很方便。

这个问题似乎没有正确答案,只有猜测,所以我会根据自己遇到这样的问题的经验提供一些解决思路:

  1. 您提到该应用程序上没有其他 activity,但我会通过查看查询日志进行三重检查。必须执行重复的查询。

  2. 可能表单不小心提交了两次。用户双击提交按钮,或者如果 UI 没有响应,他们会再次单击。您可以通过查看 Apache 日志文件了解您表单上同一时间戳的 POST 请求来检查这个想法。您可能需要实施一些 javascript 代码来防止双击您的表单页面提交按钮。

  3. 您的预感可能非常接近正确,因为存在某种竞争条件。使用事务不会阻止竞争条件,但它们确实提供了优雅回滚的方法。将您的代码包装在 try/catch 块中,以便您可以捕获 Mysql 异常并向用户显示友好错误和重试选项。