我正在使用java和Spring。作为测试,我通过 id 查询一个对象,然后尝试保存同一对象而不更新任何内容。当我这样做时,我得到了重复的键异常。根据我读到的内容,如果 _id 为 null,MongoRepository.save() 应该执行插入操作,否则执行更新操作。显然,我应该得到更新。
一些代码:
// Succeeds
Datatype sut = mongoRepository.findOne("569eac0dd4c623dc65508679");
// Fails with duplicate key.
mongoRepository.save(sut);
为什么?对其他类的对象重复上述操作,它们就可以工作了。我该如何解决这个问题?我不知道如何分解它并隔离问题。
谢谢
错误:
27906 [http-bio-8080-exec-3] 2016-05-02 13:00:26,304 DEBUG org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver -
Resolving exception from handler
[
public gov.nist.healthcare.tools.hl7.v2.igamt.lite.web.DatatypeSaveResponse
gov.nist.healthcare.tools.hl7.v2.igamt.lite.web.controller.DatatypeController.save(
gov.nist.healthcare.tools.hl7.v2.igamt.lite.domain.Datatype)
throws gov.nist.healthcare.tools.hl7.v2.igamt.lite.web.exception.DatatypeSaveException
]:
org.springframework.dao.DuplicateKeyException: {
"serverUsed" : "127.0.0.1:27017" ,
"ok" : 1 ,
"n" : 0 ,
"err" : "E11000 duplicate key error index: igl.datatype.$_id_ dup key: { : ObjectId('569eac0dd4c623dc65508679') }" ,
"code" : 11000};
nested exception is com.mongodb.MongoException$DuplicateKey: {
"serverUsed" : "127.0.0.1:27017" ,
"ok" : 1 ,
"n" : 0 ,
"err" : "E11000 duplicate key error index: igl.datatype.$_id_ dup key: { : ObjectId('569eac0dd4c623dc65508679') }" ,
"code" : 11000}
...重复
我刚刚有了一个发现。当如上所示保存时,spring 会尝试插入,即使 _id 已填充。
当保存其他对象时(未显示,但类似),spring 执行更新,并且 _id 再次填充。
为什么有区别?文档说 spring 应该在填充 _id 时更新,并在未填充时插入。
还有其他原因可能导致这种情况吗?我的对象中有什么东西?也许是我的读取转换器?
更新: 我刚刚和团队见面。经过仔细检查,我们确定不再需要读取转换器。问题通过其他方式解决。
就我而言,问题是我添加了数据模型类的版本。
@Version
private Long version;
旧文档没有,它解析为 null,MongoTemplate 判定这是一个新文档。
在这种情况下,只需使用默认值初始化版本(private Long version = 0;)。
当使用读转换器或写转换器时,您可以通过确保要保存的对象包含非空 id 字段来解决问题。
SimpleMongoRepository 在执行转换之前检查实体是否是新的。在我们的实例中,我们有一个没有 id 字段的对象,写入转换器会添加它。
向对象添加填充的 id 字段会通知 SimpleMongoRepository 调用 save 而不是 insert。
决定发生在这里。您的代码可能会因 Spring 版本而异。我希望这有帮助。
@Override
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null!");
if (entityInformation.isNew(entity)) {
return mongoOperations.insert(entity, entityInformation.getCollectionName());
}
return mongoOperations.save(entity, entityInformation.getCollectionName());
}
在数据库端,您可能已经创建了唯一索引。请查看“https://docs.mongodb.com/manual/core/index-unique/”了解更多信息。
在 Datatype 实体中实现 equals 和 hashcode 方法,并确保 mongoRepository 扩展 CrudRepositor (如https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#repositories中所述)以确保如果对象相等,则 save 方法应合并它们而不是保存一个新的。
我有类似的经历,
save()
正在抛出DuplicateKeyException
,即使我只是修改了一些字段并在修改后的版本上调用了保存。就我而言,问题是我的 @Id
字段是 long
而不是 Long
。