Spring Boot MongoDB审计出现重复键错误求助
在Spring Boot集成MongoDB审计功能(自动维护createdDate和lastModifiedDate)时,执行更新操作触发以下重复键错误:
org.springframework.dao.DuplicateKeyException: Write operation error on server user.domain.com:27017. Write error: WriteError{code=11000, message='E11000 duplicate key error collection: springboot.category index: id dup key: { _id: "21" }', details={}}.
问题原因
当前更新逻辑是直接创建新的CategoryMongo实例并设置已存在的_id(即category_id),然后调用save()方法。Spring Data MongoDB的save()默认逻辑:
- 若实体是瞬时状态(未从数据库加载、不在持久化上下文管理中),即使设置了
_id,也会执行插入操作而非更新; - 只有实体是从数据库查询得到的持久化状态实例时,
save()才会触发更新操作。
因此用新实例设置已存在的_id并调用save(),MongoDB会尝试插入重复_id的文档,触发11000错误。
另外,实体类中手动重写的getEmberId()方法返回category_id,与字段emberId定义不符,可能导致序列化/实体状态判断异常,间接影响save方法行为。
解决方案
1. 修正更新逻辑,使用持久化实体操作
不要直接创建新实例,先通过ID查询数据库中的实体,修改属性后再保存:
// 正确的更新逻辑 Optional<CategoryMongo> existingCatOpt = categoryRepositoryMongo.findById(category.getCategory_id().toString()); if (existingCatOpt.isPresent()) { CategoryMongo catm = existingCatOpt.get(); // 修改需要更新的属性 catm.setName(category.getName()); catm.setSlug(category.getSlug()); catm.setActive(category.getActive()); // 调用save执行更新(此时为持久化状态,触发update操作) categoryRepositoryMongo.save(catm); } else { // 若不存在,执行插入逻辑 CategoryMongo catm = new CategoryMongo(); catm.setName(category.getName()); catm.setSlug(category.getSlug()); catm.setActive(category.getActive()); catm.setCategory_id(category.getCategory_id().toString()); catm.setEmberId(category.getCategory_id().toString()); categoryRepositoryMongo.save(catm); }
2. 修正实体类getEmberId()逻辑
如果emberId需要映射为category_id,建议通过注解实现,而非重写getter:
@Getter @Setter @NoArgsConstructor @Document(collection = "category") public class CategoryMongo extends AuditMetadata{ @Id @JsonProperty("category_id") private String category_id; // 若emberId需要与category_id保持一致,直接通过getter映射 @JsonProperty("id_colletion") public String getEmberId() { return category_id; } @JsonProperty("category_name") private String name; @JsonProperty("category_active") private ProductEnum active = ProductEnum.ativo; @JsonProperty("category_slug") private String slug; }
3. 确保@Version字段正常工作(可选)
审计基类中的@Version用于乐观锁,更新时需使用从数据库查询的实体(自然包含version值),避免乐观锁失效。
验证要点
- 确认数据库
category集合的_id类型与实体类category_id类型一致(均为字符串); - 更新操作必须基于从数据库查询得到的实体实例进行。
内容的提问来源于stack exchange,提问作者Rafael Souza

