Doctrine ODM实现MongoDB多文档事务的替代方案及报错解决问询
Doctrine ODM 多文档事务实现方案及报错解决
报错根因
The "writeConcern" option cannot be specified within a transaction. Instead, specify it when starting the transaction.报错的根因为Doctrine ODM默认会在执行flush()操作时自动携带全局配置的writeConcern参数,而MongoDB事务规范明确禁止在事务执行过程中为单个操作指定writeConcern,该配置必须在事务启动阶段统一声明。
可行的多文档事务实现步骤
Doctrine ODM 2.0+版本已经支持将MongoDB会话传入操作方法,按照以下流程实现即可正常使用多文档事务:
- 第一步:从DocumentManager获取底层MongoDB客户端,创建会话
// 替换为项目中获取DocumentManager的实际代码 $dm = $this->getDocumentManager(); $client = $dm->getClient(); $session = $client->startSession();
- 第二步:启动事务时统一声明事务级别的配置,不要在后续单个操作中重复指定
$session->startTransaction([ 'writeConcern' => new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY), 'readConcern' => new \MongoDB\Driver\ReadConcern(\MongoDB\Driver\ReadConcern::LOCAL), 'readPreference' => new \MongoDB\Driver\ReadPreference(\MongoDB\Driver\ReadPreference::RP_PRIMARY), ]);
- 第三步:事务内所有读写操作都绑定当前会话,flush时仅传入会话参数,不要额外携带writeConcern配置
try { // 事务内写入操作示例 $newDoc = new TestDocument(); $newDoc->setField('测试值'); $dm->persist($newDoc); $dm->flush(['session' => $session]); // 事务内查询操作示例 $existDoc = $dm->getRepository(TestDocument::class)->findOneBy(['id' => 2], [], null, $session); $existDoc->setField('更新后的值'); $dm->persist($existDoc); $dm->flush(['session' => $session]); // 提交事务 $session->commitTransaction(); } catch (\Exception $e) { // 异常回滚 $session->abortTransaction(); throw $e; } finally { // 关闭会话 $session->endSession(); }
- 特殊兼容处理:如果所用Doctrine ODM版本有全局强制注入writeConcern的配置,依然触发报错,可以在flush时主动设置writeConcern为null覆盖默认配置:
$dm->flush(['session' => $session, 'writeConcern' => null]);
内容的提问来源于stack exchange,提问作者Ravish
相关产品推荐
相关产品推荐

