我有以下代码:
$count = 0;
/** @var Order $order */
foreach ($orders as $order) {
$output->write(sprintf('Processing order %s... ', ($count + 1), $order->getReference()));
if ($this->shouldSkip($order)) {
$output->writeln('Doesn\'t meet criteria, skipping.');
continue;
}
$paymentPlanName = PayoutPlanFactory::make($order)->getPayoutPlanName();
$output->write(sprintf('Setting payment plan to %s...', $paymentPlanName));
$order->setPayoutPlan($paymentPlanName);
$em->persist($order);
$output->writeln(' Done.');
if ((++$count % 1000) === 0) {
$output->write('Flushing records... ');
$em->flush();
$output->writeln('Done.');
$output->write('Clearing em... ');
$em->clear();
$output->writeln('Done.');
}
}
$em->flush();
$em->clear();
当我运行它时,输出正是我期望的前 1000 条记录。我得到了订单参考,然后是它设置的支付计划。我也查看数据库,我的记录在那里更新。但是,从第 1001 行开始的那一刻,我收到以下错误:
[Doctrine\ORM\ORMInvalidArgumentException]
A managed+dirty entity 57b6fed7b4ad7 can not be scheduled for insertion.
该 ID 与订单参考匹配。
我首先认为这可能是实体 1001 的问题,所以将我的批量大小更改为 50,然后它发生在它试图保存的第 51 个实体上。这几乎就像
$em->clear();
没有做它应该做的事。
有什么办法可以解决这个问题吗?
如果您的订单来自使用存储库功能的数据库,首先清除它们。这就是为什么在第一批 1000 之后没有任何工作了。 学说得到 mindfu****。哈哈
但你离解决方案不远了。
在你的函数之上尝试这样的东西
$orders = $this->$orderRepository->findAll();
$orderIds = array_map(function (Order $value) {
return $value->getId();
}, $orders);
// this way you only iterate over an array of ids
$this->em->clear() // detach all orders from entity manager : freeing memory
$count = 0;
foreach($orderIds => $orderId){
$order = $this->$orderRepository->find($orderId);
// your code here
}
这样您也可以实现您的目标:通过释放内存来保持大型记录的性能。
提示:
->persist($object)
仅对新实体有用。如果你的对象来自任何存储库函数,它已经被持久化了。
这个答案非常适合中大型数据集。 由于
->find()
,您的查询量将等于记录量。这对于非常大量的数据可能是不利的。由于通过 id 查询在良好和最新的数据库版本中非常快,这可能就足够了。
但是对于非常大和非常大的数据集。使用批处理方法也很简单:https://www.doctrine-project.org/projects/doctrine-orm/en/2.14/reference/batch-processing.html#iterating-results
或者
https://www.doctrine-project.org/projects/doctrine-orm/en/2.14/reference/batch-processing.html#iterating-results