DATAREST-647 - Removed unnecessary lookup of the entity to update.

RepositoryEntityController.patchItemResource(…) triggered an additional, unnecessary lookup of the original entity to be updated. However the resolution algorithm for PersistentEntityResource already checks the presence and rejects an invalid identifier with a ResourceNotFoundException.

PersistentEntityResource now carries a boolean flag to expose whether the entity is about to be created. That allows us to avoid the additional lookup in the method handling PUT requests, too.

Related tickets: DATAREST-441.
This commit is contained in:
Oliver Gierke
2015-08-18 16:47:10 +02:00
parent 87bd4450ba
commit 006c8b777a
3 changed files with 36 additions and 17 deletions

View File

@@ -41,6 +41,7 @@ public class PersistentEntityResource extends Resource<Object> {
private final PersistentEntity<?, ?> entity;
private final Iterable<EmbeddedWrapper> embeddeds;
private final boolean enforceAssociationLinks;
private final boolean isNew;
/**
* Creates a new {@link PersistentEntityResource} for the given {@link PersistentEntity}, content, embedded
@@ -53,7 +54,7 @@ public class PersistentEntityResource extends Resource<Object> {
* @param embeddeds can be {@literal null}.
*/
private PersistentEntityResource(PersistentEntity<?, ?> entity, Object content, Iterable<Link> links,
boolean renderAllAssociations, Iterable<EmbeddedWrapper> embeddeds) {
boolean renderAllAssociations, Iterable<EmbeddedWrapper> embeddeds, boolean isNew) {
super(content, links);
@@ -62,6 +63,7 @@ public class PersistentEntityResource extends Resource<Object> {
this.entity = entity;
this.embeddeds = embeddeds == null ? NO_EMBEDDEDS : embeddeds;
this.enforceAssociationLinks = renderAllAssociations;
this.isNew = isNew;
}
/**
@@ -91,6 +93,16 @@ public class PersistentEntityResource extends Resource<Object> {
return embeddeds;
}
/**
* Returns whether the content of the resource is a new entity about to be created. Used to distinguish between
* creation and updates for incoming requests.
*
* @return
*/
public boolean isNew() {
return isNew;
}
/**
* Creates a new {@link Builder} to create {@link PersistentEntityResource}s eventually.
*
@@ -174,7 +186,17 @@ public class PersistentEntityResource extends Resource<Object> {
* @return
*/
public PersistentEntityResource build() {
return new PersistentEntityResource(entity, content, links, renderAllAssociationLinks, embeddeds);
return new PersistentEntityResource(entity, content, links, renderAllAssociationLinks, embeddeds, false);
}
/**
* Finally creates the {@link PersistentEntityResource} instance to symbolize the contained entity is about to be
* created.
*
* @return
*/
public PersistentEntityResource forCreation() {
return new PersistentEntityResource(entity, content, links, renderAllAssociationLinks, embeddeds, true);
}
}
}

View File

@@ -371,13 +371,12 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
RepositoryInvoker invoker = resourceInformation.getInvoker();
Object objectToSave = incomingWrapper.getBean();
Object domainObject = invoker.invokeFindOne(id);
Object domainObject = payload.getContent();
eTag.verify(resourceInformation.getPersistentEntity(), domainObject);
return domainObject == null ? createAndReturn(objectToSave, invoker, assembler,
config.returnBodyOnCreate(acceptHeader)) : saveAndReturn(objectToSave, invoker, PUT, assembler,
config.returnBodyOnUpdate(acceptHeader));
return payload.isNew() ? createAndReturn(objectToSave, invoker, assembler, config.returnBodyOnCreate(acceptHeader))
: saveAndReturn(objectToSave, invoker, PUT, assembler, config.returnBodyOnUpdate(acceptHeader));
}
/**
@@ -402,15 +401,11 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
resourceInformation.verifySupportedMethod(HttpMethod.PATCH, ResourceType.ITEM);
Object domainObject = resourceInformation.getInvoker().invokeFindOne(id);
if (domainObject == null) {
throw new ResourceNotFoundException();
}
Object domainObject = payload.getContent();
eTag.verify(resourceInformation.getPersistentEntity(), domainObject);
return saveAndReturn(payload.getContent(), resourceInformation.getInvoker(), PATCH, assembler,
return saveAndReturn(domainObject, resourceInformation.getInvoker(), PATCH, assembler,
config.returnBodyOnUpdate(acceptHeader));
}

View File

@@ -25,6 +25,7 @@ import org.springframework.core.MethodParameter;
import org.springframework.data.repository.support.RepositoryInvoker;
import org.springframework.data.rest.webmvc.IncomingRequest;
import org.springframework.data.rest.webmvc.PersistentEntityResource;
import org.springframework.data.rest.webmvc.PersistentEntityResource.Builder;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.data.rest.webmvc.RootResourceInformation;
import org.springframework.data.rest.webmvc.json.DomainObjectReader;
@@ -122,13 +123,16 @@ public class PersistentEntityResourceHandlerMethodArgumentResolver implements Ha
}
Serializable id = idResolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
Object obj = read(resourceInformation, incoming, converter, id);
Object objectToUpdate = getObjectToUpdate(id, resourceInformation);
boolean forUpdate = objectToUpdate != null;
Object obj = read(resourceInformation, incoming, converter, objectToUpdate);
if (obj == null) {
throw new HttpMessageNotReadableException(String.format(ERROR_MESSAGE, domainType));
}
return PersistentEntityResource.build(obj, resourceInformation.getPersistentEntity()).build();
Builder build = PersistentEntityResource.build(obj, resourceInformation.getPersistentEntity());
return forUpdate ? build.build() : build.forCreation();
}
throw new HttpMessageNotReadableException(String.format(NO_CONVERTER_FOUND, domainType, contentType));
@@ -145,9 +149,7 @@ public class PersistentEntityResourceHandlerMethodArgumentResolver implements Ha
* @return
*/
private Object read(RootResourceInformation information, IncomingRequest request,
HttpMessageConverter<Object> converter, Serializable id) {
Object objectToUpdate = getObjectToUpdate(id, information);
HttpMessageConverter<Object> converter, Object objectToUpdate) {
// JSON + PATCH request
if (request.isPatchRequest() && converter instanceof MappingJackson2HttpMessageConverter) {