如何在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
相关产品推荐
相关产品推荐

