重置Doctrine Entity Manager后flush失效问题及解决方案
问题原因
当触发out of range on a integer column异常时,当前的EntityManager会进入关闭状态。调用resetManager()会生成一个全新的EntityManager实例,但之前的$delayReport实体对象仍然绑定在旧的EntityManager托管上下文里。后续操作这个对象时,新的EntityManager无法识别它的变更,调用flush()自然不会同步任何数据到数据库。
解决方案
1. 重置Manager后重新获取实体
在异常处理后,如果后续还要操作同一个实体,需要从新的EntityManager中重新查询获取对象:
public function renewDeliveryTime($delayReport) : void { try { $this->delayReportRepository->updateRenewedDeliveryTimeAt($delayReport, 50000000); }catch (\Exception $exception){ $this->managerRegistry->resetManager(); // 重新获取实体,确保绑定到新的EntityManager $delayReport = $this->delayReportRepository->find($delayReport->getId()); // 可添加降级处理逻辑 } }
2. 合并旧实体到新的EntityManager
如果不想重新查询,可用merge()方法将旧实体合并到新的EntityManager上下文:
public function renewDeliveryTime($delayReport) : void { try { $this->delayReportRepository->updateRenewedDeliveryTimeAt($delayReport, 50000000); }catch (\Exception $exception){ $newEntityManager = $this->managerRegistry->resetManager(); // 将旧实体合并到新Manager的上下文 $delayReport = $newEntityManager->merge($delayReport); } }
3. 提前校验数据避免异常
从根源上避免触发字段范围异常,设置数值前先校验是否符合数据库字段限制:
public function updateRenewedDeliveryTimeAt($delayReport,$delayDuration) { /** * @var DelayReport $delayReport */ // 假设数据库delay_duration为INT类型(范围-2147483648到2147483647) if ($delayDuration < -2147483648 || $delayDuration > 2147483647) { throw new \InvalidArgumentException('延迟时长超出字段范围'); } $delayReport->setDelayDuration($delayDuration); $delayReport->setStatus(DelayReport::STATUS['DONE']); $this->getEntityManager()->flush(); }
4. 事务回滚替代直接重置Manager
EntityManager关闭通常因事务失败导致,优先尝试回滚事务而非直接重置Manager,减少上下文切换影响:
public function renewDeliveryTime($delayReport) : void { $entityManager = $this->getEntityManager(); try { $this->delayReportRepository->updateRenewedDeliveryTimeAt($delayReport, 50000000); }catch (\Exception $exception){ if ($entityManager->isOpen()) { $entityManager->rollback(); } else { $this->managerRegistry->resetManager(); // 重新获取实体或合并 } } }
内容的提问来源于stack exchange,提问作者sina
相关产品推荐
相关产品推荐

