Spring Boot集成MongoDB出现DuplicateKeyException问题求助
这个问题我遇到过,核心原因是你的实体类中@Id字段的类型和默认值导致了重复键冲突,我来给你拆解一下并提供解决方案:
问题根源分析
从异常信息可以明确看到:
com.mongodb.DuplicateKeyException: Write failed with error code 11000 and error message 'E11000 duplicate key error collection: Football_Admin.SignUp index: _id dup key: { : 0 }'
这说明你两次插入的文档_id值都是0,违反了MongoDB对_id字段的唯一索引约束。
看你的SignUp实体类:
@Id private int id;
int类型的默认值是0,当你插入第一个文档时,如果没有显式设置id,MongoDB就会把0作为_id存储;插入第二个文档时,新创建的SignUp对象id依然是默认的0,自然触发重复键错误。
解决方案(推荐第一种)
方案1:让MongoDB自动生成唯一_id(最省心)
MongoDB原生支持自动生成全局唯一的ObjectId,Spring Data MongoDB可以直接适配这个特性,只需要修改实体类的id类型:
修改后的SignUp实体类:
package com.admin.Model; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; @Document(collection="SignUp") public class SignUp { @Id private String id; // 把int改成String类型,MongoDB会自动生成ObjectId字符串 private String fullName; private String email; private String password; // 对应修改getter和setter为String类型 public String getId() { return id; } public void setId(String id) { this.id = id; } // 其他getter/setter保持不变 public String getFullName() { return fullName; } public void setFullName(String fullName) { this.fullName = fullName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String toString() { return id + "" + fullName + "" + password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
你的SignUpRepository接口本来就是MongoRepository<SignUp,String>,刚好匹配修改后的id类型,不需要做任何改动。
之后每次调用repository.save(sign)时,如果sign.getId()为null,MongoDB会自动生成一个唯一的ObjectId字符串作为_id,彻底避免重复键问题。
方案2:手动维护自增int类型id(不推荐并发场景)
如果一定要用int类型的id,你需要自己保证每次插入的id唯一,比如查询当前最大id后加1:
- 先在
SignUpRepository中添加查询最大id的方法:
package com.admin.Repository; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.mongodb.repository.Query; import org.springframework.stereotype.Repository; import com.admin.Model.SignUp; import java.util.Optional; @Repository public interface SignUpRepository extends MongoRepository<SignUp,String>{ @Query(value = "{}", sort = "{id: -1}") Optional<Integer> findMaxId(); }
- 在Controller的
addAdmin方法中设置唯一id:
@RequestMapping(value = "/addadmin", method = RequestMethod.POST) public String addAdmin(@ModelAttribute("SignUp") SignUp sign) throws NoSuchAlgorithmException,InvalidKeySpecException { String originalPassword = sign.getPassword(); String generatedSecuredPasswordHash = generateStorngPasswordHash(originalPassword); String email = sign.getEmail(); String fullname = sign.getFullName(); try { // 手动生成唯一id Optional<Integer> maxId = repository.findMaxId(); int newId = maxId.orElse(0) + 1; sign.setId(newId); sign.setEmail(email); sign.setFullName(fullname); sign.setPassword(generatedSecuredPasswordHash); repository.save(sign); } catch (DuplicateKeyException e) { e.printStackTrace(); } // 其他打印代码不变 System.out.println(generatedSecuredPasswordHash); System.out.println("Email name is:"+sign.getEmail()); System.out.println("Full Name is:"+sign.getFullName()); System.out.println("Password is:"+sign.getPassword()); return "welcome"; }
⚠️ 注意:这种方式在高并发场景下可能出现多个请求同时拿到相同的maxId,导致重复键问题,所以只适合低并发的小型应用。
额外注意事项
如果你的MongoDB集合中已经存在_id为0的文档,需要先删除该文档或者修改它的_id值,否则即使修改了代码,第一次插入还是会触发重复键错误。
内容的提问来源于stack exchange,提问作者FIFA oneterahertz

