You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ReactiveMongo事务中用update.one实现fetchNewObject功能?

解决ReactiveMongo事务中获取更新后文档的问题

嗨,这个问题我之前也碰到过!ReactiveMongo的update.one确实只返回写操作的结果(比如更新了多少条),不会直接给你更新后的文档。不过有两个靠谱的方案可以解决这个问题,咱们一个个来看:

方案1:事务内先更新再查询

既然事务能保证操作的原子性,那咱们可以在同一个事务里先执行update.one,紧接着执行find查询来获取更新后的文档。这样就能确保你拿到的是刚更新过的数据,不会有并发问题。

修改你的函数大概是这样的:

import reactivemongo.api.bson._
import reactivemongo.api.bson.collection.BSONCollection
import reactivemongo.api.Session
import scala.concurrent.{ExecutionContext, Future}

// 假设你的case class是WhicherReport,需要提前定义对应的BSONReader
case class WhicherReport(metadata: Metadata, expenses: List[Expense], ...)
implicit val whicherReportReader: BSONDocumentReader[WhicherReport] = ???
case class Expense(id: String, paired: Boolean, ...)

def someUpdateFunc(
  collection: BSONCollection,
  metadata: Metadata,
  ids: List[String],
  session: Session
)(implicit ec: ExecutionContext): Future[WhicherReport] = {
  val updateCmd = collection.update.one(
    q = Json.obj("metadata" -> metadata, notLocked(now)),
    u = Json.obj("$set" -> Json.obj("expenses.$[elem].paired" -> true)),
    upsert = false,
    multi = false,
    arrayFilters = Seq(BSONDocument("elem.id" -> BSONDocument("$in" -> ids))),
    collation = None
  )

  // 在事务内先执行更新,再查询对应的文档
  for {
    _ <- updateCmd.withSession(session)
    maybeDoc <- collection.find(
      Json.obj("metadata" -> metadata, notLocked(now)),
      None
    ).withSession(session).one[WhicherReport]
    report <- Future.fromTry(maybeDoc.toRight(new NoSuchElementException("No document found after update")).toTry)
  } yield report
}

注意:一定要把两个操作都绑定到同一个Session上,这样才能保证它们在同一个事务里执行。

方案2:用findOneAndUpdate替代update.one(更推荐)

其实ReactiveMongo的findOneAndUpdate是支持事务的,而且它本身就有fetchNewObject参数(就是你之前用的那个!),能直接返回更新后的文档,一步到位,比方案1更简洁高效。

修改后的代码示例:

def someUpdateFunc(
  collection: BSONCollection,
  metadata: Metadata,
  ids: List[String],
  session: Session
)(implicit ec: ExecutionContext): Future[WhicherReport] = {
  collection.findOneAndUpdate(
    selector = Json.obj("metadata" -> metadata, notLocked(now)),
    update = Json.obj("$set" -> Json.obj("expenses.$[elem].paired" -> true)),
    fetchNewObject = true, // 这里就是你需要的参数!
    upsert = false,
    arrayFilters = Seq(BSONDocument("elem.id" -> BSONDocument("$in" -> ids))),
    collation = None
  ).withSession(session).map {
    case Some(updatedDoc) => updatedDoc.as[WhicherReport]
    case None => throw new NoSuchElementException("No document was updated/found")
  }
}

这个方案的好处是少了一次查询,代码更简洁,完全贴合你之前的使用习惯。

额外注意事项

  • 确保你的MongoDB版本支持事务(需要4.0及以上,且是副本集或分片集群)
  • 所有操作都要在同一个Session上下文里执行,也就是调用.withSession(session)
  • 要确保你的case class有对应的BSONDocumentReader,这样才能把MongoDB返回的文档映射成你需要的类型

内容的提问来源于stack exchange,提问作者JohnBigs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 13:42:52