正确保存数据必须同时调用persist()和flush(),前者标记实体为待处理,后者执行sql;漏掉任一环节或顺序颠倒将静默失败。

直接调用 $entityManager->persist($entity) + $entityManager->flush() 是保存数据的唯一正确路径,但漏掉任一环节或顺序颠倒就会静默失败。
persist() 和 flush() 必须成对出现
Doctrine 不是“调 persist 就写库”,它只把实体标记为“待插入/更新”,真正执行 SQL 在 flush 阶段。常见错误是只 persist 不 flush,或者在事务外 flush 了但没 commit(虽然默认 auto-commit)。
-
persist()只接受托管状态外的实体(new 实例或 detached 实例),对已 managed 实体无效 -
flush()会触发所有 pending changes:insert/update/delete,不区分单个实体 - 若实体含关联(如
$user->addPost($post)),确保关联映射配置了cascade={"persist"},否则需手动 persist 子实体
保存前必须处理 ID 和时间字段
自增主键(@ORM\GeneratedValue)无需赋值,但 datetime_immutable 类型字段不会自动设值,容易导致 NULL 插入失败。
- 构造函数里设
$this->createdAt = new \DateTimeImmutable()最稳妥 - 避免依赖
@ORM\PrePersist回调——它只在 flush 时触发,若你先 persist 再改字段再 flush,回调不会重跑 - 字段名别用
route,Symfony 内部有Route类,可能引发命名冲突或 Doctrine 解析歧义
嵌套实体保存常报 “Entity must be managed” 错误
比如 Inventory 新建时预加载 InventoryProduct 子项,子项没被 persist() 就直接 flush,Doctrine 会拒绝处理未托管的实体。
- 方案一:循环中对每个子实体显式调用
$entityManager->persist($child) - 方案二:在父实体的关联属性上加
cascade={"persist"},然后只 persist 父实体 - 注意:级联只对
persist生效,remove或merge需单独声明
最容易被忽略的是 flush 的副作用:它会刷新整个 UnitOfWork,可能意外提交其他 pending 实体;如果在长事务中多次 flush,还可能因数据库锁或并发导致部分失败。真要分步落库,优先考虑拆成多个独立事务,而不是靠单次 flush 控制粒度。











