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

Spring Boot更新MongoDB未预查询文档时如何避免DuplicateKeyException

解决方案

问题根因

  • Spring Data MongoDB 的 save() 方法默认通过规则判断实体是新增还是更新:当实体标注了 @Version 注解时,只要 version 字段为 null,就会直接判定为新实体,执行 insert 操作而非 update。
  • 你的实体ID为手动赋值,并非数据库生成,从DTO转换为实体时如果没有携带version值,转换后的实体version为null,触发insert逻辑,遇到已存在的ID就会抛出DuplicateKeyException。

可选实现方案

方案1:实现Persistable接口自定义新旧判断(适配Repository体系,推荐)

通过让基类实现Spring Data提供的Persistable接口,自行控制isNew判断逻辑,无需改动现有Repository调用逻辑。

  1. 修改抽象基类BeanA:
@Data
@SuperBuilder
@NoArgsConstructor
@Validated
public abstract class BeanA implements Persistable<Serializable> {

    @Version
    private Long version;    

    @CreatedDate
    private ZonedDateTime createdAt;

    @LastModifiedDate
    private ZonedDateTime modifiedAt;

    @CreatedBy
    private String createdBy;

    @LastModifiedBy
    private String modifiedBy;

    // 新增transient标记字段,不会持久化到MongoDB
    @Transient
    @Builder.Default
    private boolean isNewEntity = false;

    @Override
    public boolean isNew() {
        return isNewEntity;
    }
}
  1. 业务层调用时手动标记实体状态:
    • 新增场景:构造实体时将isNewEntity设为true
    • 更新场景:构造实体时将isNewEntity设为false
      修改后调用save()方法就会按照你标记的状态选择insert/update,无需提前读库。

方案2:使用MongoTemplate直接执行更新操作

无需改动现有实体结构,直接通过MongoTemplate构造更新语句,灵活度更高:

@Autowired
private MongoTemplate mongoTemplate;

public void updateBeanB(BeanB beanB) {
    // 构造查询条件,匹配复合ID
    Query query = Query.query(Criteria.where("_id").is(beanB.getCompositeId()));
    // 构造需要更新的字段
    Update update = new Update()
            .set("modifiedBy", beanB.getModifiedBy())
            .set("modifiedAt", ZonedDateTime.now())
            .inc("version", 1); // 需要乐观锁能力时添加,手动递增版本号
    // 执行更新,如需不存在则插入可替换为upsert方法
    mongoTemplate.updateFirst(query, update, BeanB.class);
}

注意事项

  • 如果需要保留@Version的乐观锁能力,更新请求的DTO必须携带当前记录的version值,否则乐观锁校验会失效,无法检测并发修改冲突。
  • 若不需要乐观锁能力,可直接删除@Version注解,Spring Data会默认根据ID是否为null判断实体状态,手动赋值ID的场景会直接走update逻辑,也不会触发重复键异常。

内容的提问来源于stack exchange,提问作者du-it

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 10:00:00