使用 Spring Data Rest 对可修改资源进行 PUT 时出现 E11000 重复键错误
E11000 duplicate key error when doing PUT for modifiable resource with Spring Data Rest
更新:
根据问题,Spring数据的作者说,@Version属性将成为响应header的ETags。更新有两个选项:
Just PUT without an If-Match header -- 强制覆盖服务器上存在的任何内容,因为聚合被加载,传入数据映射到它并写回。如果另一个客户端同时更改了聚合(尽管公认的非常短 window),您仍然会应用乐观锁定。如果是这种情况,您将看到 409 冲突。
我目前正在使用这种方式进行PUT,我得到的是409冲突。但是异常似乎不是乐观锁定。并且假设不会有另一个客户同时更改聚合。
PUT with an If-Match header - Spring Data REST 检查提交的 ETag 与聚合版本 属性 的当前值和 return 412 先决条件失败,以防此时不匹配。
我尝试添加 If-Match header(If-Match: "0" or If-Match: 0),但它们都出现 500 内部服务器错误。异常如下,如果我得到它,我已经检查我 PUT 的资源在数据库和 ETags 中的当前版本为“0”以响应 header。
java.lang.NullPointerException: 空
在 org.springframework.data.rest.webmvc.support.ETag.getVersionInformation(ETag.java:192) ~[spring-data-rest-webmvc-2.4.1.RELEASE.jar:na]
在 org.springframework.data.rest.webmvc.support.ETag.from(ETag.java:76) ~[spring-data-rest-webmvc-2.4.1.RELEASE.jar:na]
在 org.springframework.data.rest.webmvc.support.ETag.verify(ETag.java:94) ~[spring-data-rest-webmvc-2.4.1.RELEASE.jar:na]
在 org.springframework.data.rest.webmvc.RepositoryEntityController.putItemResource(RepositoryEntityController.java:410) ~[spring-data-rest-webmvc-2.4.1.RELEASE.jar:na]
在 sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法)~[na:1.8.0_45]
在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_45]
在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_45]
在 java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_45]
在 org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:222) ~[spring-web-4.2.4.RELEASE.jar:4.2.4.RELEASE]
在 org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137) ~[spring-web-4.2.4.RELEASE.jar:4.2.4.RELEASE]
在 org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) ~[spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
在 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:814) ~[spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
在 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:737) ~[spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
在 org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
在 org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959) ~[spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
在 org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) ~[spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
在 org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:969) [spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
在 org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:882) [spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
根据问题,Spring数据的作者说,使用Spring数据JPA,你需要使用@javax.persistence.Version。 @org.springframework.data.annotation.Version 是用于其他 Spring 数据模块的注解。
我尝试更改为使用@javax.persistence.Version,但版本属性将不再成为 header 中的 ETag,它将显示在 "version" 字段中以响应 [=150] =].同时,如果我没有 If-Match,它会成功(200),但版本字段将被删除。如果我输入 If-Match: 0,它会得到 412 Precondition Failed。不管怎样,我不是用spring-data-jpa,我用的是spring-boot-starter-data-rest和spring-boot-starter-data-mongodb。因此,我认为我的@Version 的正确方法是 org.springframework.data.annotation.Version in Spring Data Common.
我们有一个 REST API 网络应用程序,由 Spring Data Rest 框架和 MongoDB。还有一个@document
classDeliveryOrder
@Document(collection = "delivery_orders")
public class DeliveryOrder extends PersistableDocument {
private static final long serialVersionUID = 1L;
@LastModifiedBy
protected String lastModifiedBy;
@LastModifiedDate
@DateTimeFormat(iso = ISO.DATE_TIME)
protected Instant lastModifiedDate;
@Version
protected Long version;
protected Map<String, Object> dyna;
...
}
以及如下所示的 PersistableDocument
public abstract class PersistableDocument {
private static final long serialVersionUID = 1L;
@Id
protected String id;
@CreatedBy
protected String createdBy;
@CreatedDate
@DateTimeFormat(iso = ISO.DATE_TIME)
@Indexed(direction = IndexDirection.ASCENDING, unique = false)
protected Instant createdDate;
...
}
如果我 PUT 已经存在的资源 (URI: https://${HOST}/${APPLICATION_NAMEAME}/${Object_Id}) 将得到 409 冲突和异常如下。但是如果我删除了 @Version 字段,它将作为 PUT 方法工作。似乎不是因为 _id 字段重复了。可能是一些乐观锁定问题?
感谢任何帮助
2016-05-04 17:38:34.413 ERROR 8668 --- [nio-1010-exec-2]
o.s.d.r.w.RepositoryRestExceptionHandler : Write failed with error code 11000 and error message 'E11000 duplicate key error collection: 56cbfbd323f5496dcc02c579.delivery_orders index: _id_ dup key: { : ObjectId('56d6b81623f54966b07b0587') }'; nested exception is com.mongodb.DuplicateKeyException: Write failed with error code 11000 and error message 'E11000 duplicate key error collection: 56cbfbd323f5496dcc02c579.delivery_orders index: _id_ dup key: { : ObjectId('56d6b81623f54966b07b0587') }'
org.springframework.dao.DuplicateKeyException: Write failed with error code 11000 and error message 'E11000 duplicate key error collection: 56cbfbd323f5496dcc02c579.delivery_orders index: _id_ dup key: { : ObjectId('56d6b81623f54966b07b0587') }'; nested exception is com.mongodb.DuplicateKeyException: Write failed with error code 11000 and error message 'E11000 duplicate key error collection: 56cbfbd323f5496dcc02c579.delivery_orders index: _id_ dup key: { : ObjectId('56d6b81623f54966b07b0587') }'
at org.springframework.data.mongodb.core.MongoExceptionTranslator.translateExceptionIfPossible(MongoExceptionTranslator.java:71) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.potentiallyConvertRuntimeException(MongoTemplate.java:2060) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:464) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.insertDBObject(MongoTemplate.java:985) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.doInsert(MongoTemplate.java:798) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.doSaveVersioned(MongoTemplate.java:941) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.save(MongoTemplate.java:925) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.save(SimpleMongoRepository.java:78) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_45]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_45]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_45]
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_45]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:483) ~[spring-data-commons-1.11.1.RELEASE.jar:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:468) ~[spring-data-commons-1.11.1.RELEASE.jar:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:440) ~[spring-data-commons-1.11.1.RELEASE.jar:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61) ~[spring-data-commons-1.11.1.RELEASE.jar:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at com.sun.proxy.$Proxy122.save(Unknown Source) ~[na:na]
at org.springframework.data.repository.support.CrudRepositoryInvoker.invokeSave(CrudRepositoryInvoker.java:100) ~[spring-data-commons-1.11.1.RELEASE.jar:na]
at org.springframework.data.rest.core.support.UnwrappingRepositoryInvokerFactory$UnwrappingRepositoryInvoker.invokeSave(UnwrappingRepositoryInvokerFactory.java:225) ~[spring-data-rest-core-2.4.1.RELEASE.jar:na]
at org.springframework.data.rest.webmvc.RepositoryEntityController.saveAndReturn(RepositoryEntityController.java:491) ~[spring-data-rest-webmvc-2.4.1.RELEASE.jar:na]
at org.springframework.data.rest.webmvc.RepositoryEntityController.putItemResource(RepositoryEntityController.java:413) ~[spring-data-rest-webmvc-2.4.1.RELEASE.jar:na]
Caused by: com.mongodb.DuplicateKeyException: Write failed with error code 11000 and error message 'E11000 duplicate key error collection: 56cbfbd323f5496dcc02c579.delivery_orders index: _id_ dup key: { : ObjectId('56d6b81623f54966b07b0587') }'
at com.mongodb.operation.BaseWriteOperation.convertBulkWriteException(BaseWriteOperation.java:236) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.operation.BaseWriteOperation.access0(BaseWriteOperation.java:60) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.operation.BaseWriteOperation.call(BaseWriteOperation.java:146) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.operation.BaseWriteOperation.call(BaseWriteOperation.java:133) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.operation.OperationHelper.withConnectionSource(OperationHelper.java:230) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.operation.OperationHelper.withConnection(OperationHelper.java:221) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.operation.BaseWriteOperation.execute(BaseWriteOperation.java:133) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.operation.BaseWriteOperation.execute(BaseWriteOperation.java:60) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.Mongo.execute(Mongo.java:782) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.Mongo.execute(Mongo.java:765) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.DBCollection.executeWriteOperation(DBCollection.java:333) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.DBCollection.insert(DBCollection.java:328) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.DBCollection.insert(DBCollection.java:319) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.DBCollection.insert(DBCollection.java:289) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.DBCollection.insert(DBCollection.java:255) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.DBCollection.insert(DBCollection.java:192) ~[mongo-java-driver-3.2.0.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.doInCollection(MongoTemplate.java:990) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:462) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
... 117 common frames omitted
我终于找到了根本原因。那是因为我没有在我的PUT body中给出version字段,Spring-data-rest可能被认为对文档做了一些插入操作,因此会抛出E11000 duplicate key error异常.
但是,主要问题是我不会有版本字段 setter 的实现,如下所示:
public void setVersion(Long version)
{
this.version = version;
}
如果您的 PUT 请求正文中没有版本字段,则版本参数将为 null。只需删除此 setter 或使用 @JsonIgnore 注释即可解决此问题。 Spring-data-rest 会处理剩下的事情。
一个不直接解决 OP 问题的备选答案,但其他答案可能 运行。
如果您在 @RepositoryRestController
PUT 方法中覆盖 SDR,您的前端不太可能发送 version
属性,就像 SPD 不太可能发送version
属性。
我不确定 正确的 解决方案是什么,但我们决定从 header 中提取 etag
并将其放在模型上在向我们的自定义控制器提交 PUT
请求之前。
if (obj && header.etag && _.isInteger(header.etag)) {
obj.version = parseInt(header.etag, 10);
}
对于现有的行,我手动添加了我的 collection 版本号
db.getCollection('myCollection').updateMany({}, {$set:{version: NumberLong(0)}})
在我的文档中我只有
@Version
private Long version;
更新:
根据
Just PUT without an If-Match header -- 强制覆盖服务器上存在的任何内容,因为聚合被加载,传入数据映射到它并写回。如果另一个客户端同时更改了聚合(尽管公认的非常短 window),您仍然会应用乐观锁定。如果是这种情况,您将看到 409 冲突。
我目前正在使用这种方式进行PUT,我得到的是409冲突。但是异常似乎不是乐观锁定。并且假设不会有另一个客户同时更改聚合。
PUT with an If-Match header - Spring Data REST 检查提交的 ETag 与聚合版本 属性 的当前值和 return 412 先决条件失败,以防此时不匹配。
我尝试添加 If-Match header(If-Match: "0" or If-Match: 0),但它们都出现 500 内部服务器错误。异常如下,如果我得到它,我已经检查我 PUT 的资源在数据库和 ETags 中的当前版本为“0”以响应 header。
java.lang.NullPointerException: 空 在 org.springframework.data.rest.webmvc.support.ETag.getVersionInformation(ETag.java:192) ~[spring-data-rest-webmvc-2.4.1.RELEASE.jar:na] 在 org.springframework.data.rest.webmvc.support.ETag.from(ETag.java:76) ~[spring-data-rest-webmvc-2.4.1.RELEASE.jar:na] 在 org.springframework.data.rest.webmvc.support.ETag.verify(ETag.java:94) ~[spring-data-rest-webmvc-2.4.1.RELEASE.jar:na] 在 org.springframework.data.rest.webmvc.RepositoryEntityController.putItemResource(RepositoryEntityController.java:410) ~[spring-data-rest-webmvc-2.4.1.RELEASE.jar:na] 在 sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法)~[na:1.8.0_45] 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_45] 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_45] 在 java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_45] 在 org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:222) ~[spring-web-4.2.4.RELEASE.jar:4.2.4.RELEASE] 在 org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137) ~[spring-web-4.2.4.RELEASE.jar:4.2.4.RELEASE] 在 org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) ~[spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE] 在 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:814) ~[spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE] 在 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:737) ~[spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE] 在 org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE] 在 org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959) ~[spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE] 在 org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) ~[spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE] 在 org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:969) [spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE] 在 org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:882) [spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
根据
我尝试更改为使用@javax.persistence.Version,但版本属性将不再成为 header 中的 ETag,它将显示在 "version" 字段中以响应 [=150] =].同时,如果我没有 If-Match,它会成功(200),但版本字段将被删除。如果我输入 If-Match: 0,它会得到 412 Precondition Failed。不管怎样,我不是用spring-data-jpa,我用的是spring-boot-starter-data-rest和spring-boot-starter-data-mongodb。因此,我认为我的@Version 的正确方法是 org.springframework.data.annotation.Version in Spring Data Common.
我们有一个 REST API 网络应用程序,由 Spring Data Rest 框架和 MongoDB。还有一个@document
classDeliveryOrder
@Document(collection = "delivery_orders")
public class DeliveryOrder extends PersistableDocument {
private static final long serialVersionUID = 1L;
@LastModifiedBy
protected String lastModifiedBy;
@LastModifiedDate
@DateTimeFormat(iso = ISO.DATE_TIME)
protected Instant lastModifiedDate;
@Version
protected Long version;
protected Map<String, Object> dyna;
...
}
以及如下所示的 PersistableDocument
public abstract class PersistableDocument {
private static final long serialVersionUID = 1L;
@Id
protected String id;
@CreatedBy
protected String createdBy;
@CreatedDate
@DateTimeFormat(iso = ISO.DATE_TIME)
@Indexed(direction = IndexDirection.ASCENDING, unique = false)
protected Instant createdDate;
...
}
如果我 PUT 已经存在的资源 (URI: https://${HOST}/${APPLICATION_NAMEAME}/${Object_Id}) 将得到 409 冲突和异常如下。但是如果我删除了 @Version 字段,它将作为 PUT 方法工作。似乎不是因为 _id 字段重复了。可能是一些乐观锁定问题?
感谢任何帮助
2016-05-04 17:38:34.413 ERROR 8668 --- [nio-1010-exec-2]
o.s.d.r.w.RepositoryRestExceptionHandler : Write failed with error code 11000 and error message 'E11000 duplicate key error collection: 56cbfbd323f5496dcc02c579.delivery_orders index: _id_ dup key: { : ObjectId('56d6b81623f54966b07b0587') }'; nested exception is com.mongodb.DuplicateKeyException: Write failed with error code 11000 and error message 'E11000 duplicate key error collection: 56cbfbd323f5496dcc02c579.delivery_orders index: _id_ dup key: { : ObjectId('56d6b81623f54966b07b0587') }'
org.springframework.dao.DuplicateKeyException: Write failed with error code 11000 and error message 'E11000 duplicate key error collection: 56cbfbd323f5496dcc02c579.delivery_orders index: _id_ dup key: { : ObjectId('56d6b81623f54966b07b0587') }'; nested exception is com.mongodb.DuplicateKeyException: Write failed with error code 11000 and error message 'E11000 duplicate key error collection: 56cbfbd323f5496dcc02c579.delivery_orders index: _id_ dup key: { : ObjectId('56d6b81623f54966b07b0587') }'
at org.springframework.data.mongodb.core.MongoExceptionTranslator.translateExceptionIfPossible(MongoExceptionTranslator.java:71) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.potentiallyConvertRuntimeException(MongoTemplate.java:2060) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:464) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.insertDBObject(MongoTemplate.java:985) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.doInsert(MongoTemplate.java:798) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.doSaveVersioned(MongoTemplate.java:941) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.save(MongoTemplate.java:925) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.save(SimpleMongoRepository.java:78) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_45]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_45]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_45]
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_45]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:483) ~[spring-data-commons-1.11.1.RELEASE.jar:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:468) ~[spring-data-commons-1.11.1.RELEASE.jar:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:440) ~[spring-data-commons-1.11.1.RELEASE.jar:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61) ~[spring-data-commons-1.11.1.RELEASE.jar:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at com.sun.proxy.$Proxy122.save(Unknown Source) ~[na:na]
at org.springframework.data.repository.support.CrudRepositoryInvoker.invokeSave(CrudRepositoryInvoker.java:100) ~[spring-data-commons-1.11.1.RELEASE.jar:na]
at org.springframework.data.rest.core.support.UnwrappingRepositoryInvokerFactory$UnwrappingRepositoryInvoker.invokeSave(UnwrappingRepositoryInvokerFactory.java:225) ~[spring-data-rest-core-2.4.1.RELEASE.jar:na]
at org.springframework.data.rest.webmvc.RepositoryEntityController.saveAndReturn(RepositoryEntityController.java:491) ~[spring-data-rest-webmvc-2.4.1.RELEASE.jar:na]
at org.springframework.data.rest.webmvc.RepositoryEntityController.putItemResource(RepositoryEntityController.java:413) ~[spring-data-rest-webmvc-2.4.1.RELEASE.jar:na]
Caused by: com.mongodb.DuplicateKeyException: Write failed with error code 11000 and error message 'E11000 duplicate key error collection: 56cbfbd323f5496dcc02c579.delivery_orders index: _id_ dup key: { : ObjectId('56d6b81623f54966b07b0587') }'
at com.mongodb.operation.BaseWriteOperation.convertBulkWriteException(BaseWriteOperation.java:236) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.operation.BaseWriteOperation.access0(BaseWriteOperation.java:60) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.operation.BaseWriteOperation.call(BaseWriteOperation.java:146) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.operation.BaseWriteOperation.call(BaseWriteOperation.java:133) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.operation.OperationHelper.withConnectionSource(OperationHelper.java:230) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.operation.OperationHelper.withConnection(OperationHelper.java:221) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.operation.BaseWriteOperation.execute(BaseWriteOperation.java:133) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.operation.BaseWriteOperation.execute(BaseWriteOperation.java:60) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.Mongo.execute(Mongo.java:782) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.Mongo.execute(Mongo.java:765) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.DBCollection.executeWriteOperation(DBCollection.java:333) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.DBCollection.insert(DBCollection.java:328) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.DBCollection.insert(DBCollection.java:319) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.DBCollection.insert(DBCollection.java:289) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.DBCollection.insert(DBCollection.java:255) ~[mongo-java-driver-3.2.0.jar:na]
at com.mongodb.DBCollection.insert(DBCollection.java:192) ~[mongo-java-driver-3.2.0.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.doInCollection(MongoTemplate.java:990) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:462) ~[spring-data-mongodb-1.8.1.RELEASE.jar:na]
... 117 common frames omitted
我终于找到了根本原因。那是因为我没有在我的PUT body中给出version字段,Spring-data-rest可能被认为对文档做了一些插入操作,因此会抛出E11000 duplicate key error异常.
但是,主要问题是我不会有版本字段 setter 的实现,如下所示:
public void setVersion(Long version)
{
this.version = version;
}
如果您的 PUT 请求正文中没有版本字段,则版本参数将为 null。只需删除此 setter 或使用 @JsonIgnore 注释即可解决此问题。 Spring-data-rest 会处理剩下的事情。
一个不直接解决 OP 问题的备选答案,但其他答案可能 运行。
如果您在 @RepositoryRestController
PUT 方法中覆盖 SDR,您的前端不太可能发送 version
属性,就像 SPD 不太可能发送version
属性。
我不确定 正确的 解决方案是什么,但我们决定从 header 中提取 etag
并将其放在模型上在向我们的自定义控制器提交 PUT
请求之前。
if (obj && header.etag && _.isInteger(header.etag)) {
obj.version = parseInt(header.etag, 10);
}
对于现有的行,我手动添加了我的 collection 版本号
db.getCollection('myCollection').updateMany({}, {$set:{version: NumberLong(0)}})
在我的文档中我只有
@Version
private Long version;