symfony 使用Doctrine将实体更新持久化到数据库时出现唯一错误

5ssjco0h  于 2022-11-16  发布在  其他
关注(0)|答案(1)|浏览(101)

我在Symfony项目中使用Doctrine来管理应用程序中的持久层。
最近,我在将实体的更改保存到数据库时遇到了一些问题。我遇到的问题是,当我更新实体并将其保存到数据库时,有时EntityManager会将我的实体视为新对象,因此它不会执行更新操作,而是执行插入操作,从而在数据库中导致了一个独特的异常错误。
正如文档所述,更新对象时,您应该只执行以下步骤:

  • 从Doctrine中提取对象
  • 修改对象
    • (可选)* 在实体管理器上调用persist()
  • 在实体管理器上调用flush()

注我在persist()调用中添加了 (可选),因为正如文档所述,由于Doctrine已经在监视对象的更改,因此没有必要添加 (可选)
现在事情已经解释清楚了,下面是我在代码中所做的工作:

$myEntity = $this->myEntityRepository->byId($id);

// make some changes to the entity
$myEntity->setSomething('something');

$this->myEntityRepository->save($entity);

其中,我的存储库中的save()操作如下所示:

$this->entityManager->persist($entity);
$this->entityManager->flush();

byId()运算:

return $this->entityManager->getRepository()->find($id);

正如我所说的,只有在持久化新实体时才应该调用持久化操作,但是由于Doctrine可以区分已经托管的实体和新实体,所以应该没有问题。如果我不调用persist()方法,而不是执行和插入操作并导致唯一的违例,它实际上什么也不做,因为它不会检测到我的操作的任何更改。
我总是使用persist()方法的原因是,我的存储库中的save()操作既用于新实体,也用于对现有实体的更新。
正如我在another answer中所看到的,调用merge()操作而不是persist()应该可以解决问题,但我认为这是不正确的,因为我认为这只是一个“肮脏”的解决方案,加上Doctrine未来版本中的方法is being deprecated
那么,我在这里遗漏了什么呢?为什么在运行上面的代码时,有时我会得到一个独特的错误呢?我在我的应用程序中只配置了一个连接和一个实体管理器。
我想补充的是,这个问题只出现在消费者(异步事件)中执行的代码中,而不是在API本身中,但每当我收到一个新事件时,都会创建一个新的数据库连接,以确保我不会遇到与以前某个事件中使用的实体管理器重叠的问题。
当谈到消费者时,我的意思是通过RabbitMQ发布一个事件(事件包含实体的ID),然后在一个独立于API的进程中消费它,直接使用实体管理器存储库获取实体。
我的猜测是,在我从存储库中获取实体的那一行(即,我使用find()方法)和我将其保存到数据库中的那一行(即,我使用flush()方法)之间,实体管理器以某种方式将实体从其UnitOfWork中删除,因此它将其视为新实体,而不是托管实体。

aemubtdh

aemubtdh1#

我最近在一些代码中发现的一个问题是实体在哪里被传递-通过RabbitMq消息和从头创建的实体,包括id --$x = new Entity(); $x->setId($data['id');$x->setName($data['name'); # ... etc)。
然而,设置id并不会使实体被Doctrine“管理”--只会阅读原始记录(从DB),然后 * 更新它。如上所述持久化$x将创建一个新记录--并忽略设置的id。
我将确保(也许在记录上使用一些Assert,您正在获取的必须通过--或者至少现在因错误而终止),以便知道您总是在处理一个已知的实体(我还假设您的->byId($id)调用在内部只是一个->find($id))。

相关问题