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

Spring Data MongoDB中如何单轮询实现Foo文档的Upsert操作?

问题描述

说明:示例基于Kotlin语言的Spring Data MongoDB,使用官方文档中描述的更新方法

请参考以下实体类与仓库代码:

@Document
data class Foo(
    val name: String,
    val bars: Set<String> = setOf(),
    @Id
    val id: String = UUID.randomUUID().toString(),
)

@Repository
interface FooRepository : CoroutineCrudRepository<Foo, String> {
    suspend fun saveOrAddBarsFrom(foo: Foo) {
        if (findAndAddToSetBarsById(foo.id, foo.bars) == 0) {
            save(foo)
        }
    }

    @Update("{ \$addToSet : { 'bars' : { \$each :  ?1 } } }")
    suspend fun findAndAddToSetBarsById(id: String, bars: Set<String>): Int
}

可见saveOrAddBarsFrom实现Foo的Upsert需两次数据库往返:先尝试对已存在Foo执行$addToSet追加字符串到bars集合,未找到则执行save操作。

能否编写方法,通过单次数据库往返实现:Foo不存在则保存,存在则向bars集合追加字符串?

解决方案

可以利用MongoDB的upsert特性,结合Spring Data MongoDB的@Update注解实现单次请求完成Upsert逻辑。核心是用$setOnInsert指定插入时的字段赋值,用$addToSet处理更新时的bars集合追加,同时开启upsert = true:

@Repository
interface FooRepository : CoroutineCrudRepository<Foo, String> {
    @Update(
        value = "{ \$setOnInsert: { 'name': ?1 }, \$addToSet: { 'bars': { \$each: ?2 } } }",
        upsert = true
    )
    suspend fun upsertFooById(id: String, name: String, bars: Set<String>): UpdateResult
}

逻辑说明

  • Foo不存在时:数据库执行插入操作,$setOnInsert会设置name字段,$addToSet将传入的bars元素添加到新文档中,最终生成的文档完全符合实体类定义。
  • Foo已存在时:$setOnInsert不会执行任何操作,仅$addToSet会把传入的新bars元素追加到已有集合中(自动去重,契合Set的特性)。

使用示例

直接传入对应参数调用即可:

// 基于已有foo实例调用
fooRepository.upsertFooById(foo.id, foo.name, foo.bars)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 03:53:11