Browse Source

Add support for modifying documents via repository method.

We now support findAndModify operations on derived query methods.

Closes: #2107
Original Pull Request: #284
pull/3970/head
Thomas Darimont 11 years ago committed by Christoph Strobl
parent
commit
6e3e8210d0
No known key found for this signature in database
GPG Key ID: 8CC1AB53391458C8
  1. 60
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
  2. 8
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java
  3. 9
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameterAccessor.java
  4. 13
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameters.java
  5. 8
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessor.java
  6. 8
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java
  7. 32
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java
  8. 10
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java
  9. 5
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java
  10. 6
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java

60
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java

@ -17,12 +17,14 @@ package org.springframework.data.mongodb.repository.query; @@ -17,12 +17,14 @@ package org.springframework.data.mongodb.repository.query;
import org.bson.Document;
import org.bson.codecs.configuration.CodecRegistry;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind;
import org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithQuery;
import org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFind;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.DeleteExecution;
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.GeoNearExecution;
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.PagedExecution;
@ -145,6 +147,11 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { @@ -145,6 +147,11 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
} else if (method.isStreamQuery()) {
return q -> operation.matching(q).stream();
} else if (method.isCollectionQuery()) {
if (method.isModifyingQuery()) {
return q -> new UpdatingCollectionExecution(accessor.getPageable(), accessor.getUpdate()).execute(q);
}
return q -> operation.matching(q.with(accessor.getPageable()).with(accessor.getSort())).all();
} else if (method.isPageQuery()) {
return new PagedExecution(operation, accessor.getPageable());
@ -155,6 +162,10 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { @@ -155,6 +162,10 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
} else {
return q -> {
if (method.isModifyingQuery()) {
return new UpdatingSingleEntityExecution(accessor.getUpdate()).execute(q);
}
TerminatingFind<?> find = operation.matching(q);
return isLimiting() ? find.firstValue() : find.oneValue();
};
@ -275,4 +286,53 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { @@ -275,4 +286,53 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
* @since 2.0.4
*/
protected abstract boolean isLimiting();
/**
* {@link MongoQueryExecution} for collection returning find and update queries.
*
* @author Thomas Darimont
*/
final class UpdatingCollectionExecution implements MongoQueryExecution {
private final Pageable pageable;
private final Update update;
UpdatingCollectionExecution(Pageable pageable, Update update) {
this.pageable = pageable;
this.update = update;
}
@Override
public Object execute(Query query) {
MongoEntityMetadata<?> metadata = method.getEntityInformation();
return operations.findAndModify(query.with(pageable), update, metadata.getJavaType(),
metadata.getCollectionName());
}
}
/**
* {@link MongoQueryExecution} to return a single entity with update.
*
* @author Thomas Darimont
*/
final class UpdatingSingleEntityExecution implements MongoQueryExecution {
private final Update update;
private UpdatingSingleEntityExecution(Update update) {
this.update = update;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.core.query.Query)
*/
@Override
public Object execute(Query query) {
MongoEntityMetadata<?> metadata = method.getEntityInformation();
return operations.findAndModify(query.limit(1), update, metadata.getJavaType(), metadata.getCollectionName());
}
}
}

8
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java

@ -31,6 +31,7 @@ import org.springframework.data.mongodb.core.convert.MongoWriter; @@ -31,6 +31,7 @@ import org.springframework.data.mongodb.core.convert.MongoWriter;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.TextCriteria;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
@ -297,4 +298,11 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor { @@ -297,4 +298,11 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
Object nextConverted(MongoPersistentProperty property);
}
/* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getUpdate()
*/
@Override
public Update getUpdate() {
return delegate.getUpdate();
}
}

9
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameterAccessor.java

@ -20,6 +20,7 @@ import org.springframework.data.geo.Distance; @@ -20,6 +20,7 @@ import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.TextCriteria;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.lang.Nullable;
@ -74,4 +75,12 @@ public interface MongoParameterAccessor extends ParameterAccessor { @@ -74,4 +75,12 @@ public interface MongoParameterAccessor extends ParameterAccessor {
* @since 1.8
*/
Object[] getValues();
/**
* Returns the {@link Update} to be used for findAndUpdate query.
*
* @return
* @since 1.7
*/
Update getUpdate();
}

13
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameters.java

@ -25,6 +25,7 @@ import org.springframework.data.geo.Distance; @@ -25,6 +25,7 @@ import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.TextCriteria;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.repository.Near;
import org.springframework.data.mongodb.repository.query.MongoParameters.MongoParameter;
import org.springframework.data.repository.query.Parameter;
@ -39,6 +40,7 @@ import org.springframework.lang.Nullable; @@ -39,6 +40,7 @@ import org.springframework.lang.Nullable;
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
* @author Thomas Darimont
*/
public class MongoParameters extends Parameters<MongoParameters, MongoParameter> {
@ -47,6 +49,7 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter> @@ -47,6 +49,7 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
private final @Nullable Integer fullTextIndex;
private final @Nullable Integer nearIndex;
private final @Nullable Integer collationIndex;
private final int updateIndex;
/**
* Creates a new {@link MongoParameters} instance from the given {@link Method} and {@link MongoQueryMethod}.
@ -67,6 +70,7 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter> @@ -67,6 +70,7 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
this.rangeIndex = getTypeIndex(parameterTypeInfo, Range.class, Distance.class);
this.maxDistanceIndex = this.rangeIndex == -1 ? getTypeIndex(parameterTypeInfo, Distance.class, null) : -1;
this.collationIndex = getTypeIndex(parameterTypeInfo, Collation.class, null);
this.updateIndex = parameterTypes.indexOf(Update.class);
int index = findNearIndexInParameters(method);
if (index == -1 && isGeoNearMethod) {
@ -77,7 +81,7 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter> @@ -77,7 +81,7 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
}
private MongoParameters(List<MongoParameter> parameters, int maxDistanceIndex, @Nullable Integer nearIndex,
@Nullable Integer fullTextIndex, int rangeIndex, @Nullable Integer collationIndex) {
@Nullable Integer fullTextIndex, int rangeIndex, @Nullable Integer collationIndex, int updateIndex) {
super(parameters);
@ -86,6 +90,7 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter> @@ -86,6 +90,7 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
this.maxDistanceIndex = maxDistanceIndex;
this.rangeIndex = rangeIndex;
this.collationIndex = collationIndex;
this.updateIndex = updateIndex;
}
private final int getNearIndex(List<Class<?>> parameterTypes) {
@ -202,7 +207,7 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter> @@ -202,7 +207,7 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
@Override
protected MongoParameters createFrom(List<MongoParameter> parameters) {
return new MongoParameters(parameters, this.maxDistanceIndex, this.nearIndex, this.fullTextIndex, this.rangeIndex,
this.collationIndex);
this.collationIndex, this.updateIndex);
}
private int getTypeIndex(List<TypeInformation<?>> parameterTypes, Class<?> type, @Nullable Class<?> componentType) {
@ -273,7 +278,9 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter> @@ -273,7 +278,9 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
private boolean hasNearAnnotation() {
return parameter.getParameterAnnotation(Near.class) != null;
}
}
public int getUpdateIndex() {
return updateIndex;
}
}

8
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessor.java

@ -22,6 +22,7 @@ import org.springframework.data.geo.Point; @@ -22,6 +22,7 @@ import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Term;
import org.springframework.data.mongodb.core.query.TextCriteria;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@ -153,4 +154,11 @@ public class MongoParametersParameterAccessor extends ParametersParameterAccesso @@ -153,4 +154,11 @@ public class MongoParametersParameterAccessor extends ParametersParameterAccesso
public Object[] getValues() {
return super.getValues();
}
@Override
public Update getUpdate() {
int updateIndex = method.getParameters().getUpdateIndex();
return updateIndex == -1 ? null : (Update) getValue(updateIndex);
}
}

8
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java

@ -30,6 +30,7 @@ import org.springframework.data.geo.GeoResults; @@ -30,6 +30,7 @@ import org.springframework.data.geo.GeoResults;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.repository.Aggregation;
import org.springframework.data.mongodb.repository.Meta;
import org.springframework.data.mongodb.repository.Query;
@ -398,4 +399,11 @@ public class MongoQueryMethod extends QueryMethod { @@ -398,4 +399,11 @@ public class MongoQueryMethod extends QueryMethod {
return (Optional<A>) this.annotationCache.computeIfAbsent(annotationType,
it -> Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(method, it)));
}
@Override
public boolean isModifyingQuery() {
Class<?>[] parameterTypes = this.method.getParameterTypes();
return parameterTypes.length > 0 && parameterTypes[parameterTypes.length - 1] == Update.class;
}
}

32
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java

@ -1473,4 +1473,36 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -1473,4 +1473,36 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
assertThat(result.getAddress()).isPresent();
assertThat(result.getFirstname()).contains("Carter");
}
/**
* @see DATAMONGO-1188
*/
@Test
public void shouldSupportFindAndModfiyForQueryDerivationWithCollectionResult() {
List<Person> result = repository.findAndModifyByFirstname("Dave", new Update().inc("visits", 42));
assertThat(result.size()).isOne();
assertThat(result.get(0)).isEqualTo(dave);
Person dave = repository.findById(result.get(0).getId()).get();
assertThat(dave.visits).isEqualTo(42);
}
/**
* @see DATAMONGO-1188
*/
@Test
public void shouldSupportFindAndModfiyForQueryDerivationWithSingleResult() {
Person result = repository.findOneAndModifyByFirstname("Dave", new Update().inc("visits", 1337));
assertThat(result).isEqualTo(dave);
Person dave = repository.findById(result.getId()).get();
assertThat(dave.visits).isEqualTo(1337);
}
}

10
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java

@ -78,6 +78,8 @@ public class Person extends Contact { @@ -78,6 +78,8 @@ public class Person extends Contact {
@DocumentReference
User spiritAnimal;
int visits;
public Person() {
this(null, null);
@ -265,6 +267,14 @@ public class Person extends Contact { @@ -265,6 +267,14 @@ public class Person extends Contact {
this.coworker = coworker;
}
public int getVisits() {
return visits;
}
public void setVisits(int visits) {
this.visits = visits;
}
/*
* (non-Javadoc)
*

5
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java

@ -36,6 +36,7 @@ import org.springframework.data.geo.GeoResults; @@ -36,6 +36,7 @@ import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.geo.Polygon;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.repository.Person.Sex;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.query.Param;
@ -419,6 +420,10 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query @@ -419,6 +420,10 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
List<Person> findByUnwrappedUser(User user);
List<Person> findAndModifyByFirstname(String firstname, Update update);
Person findOneAndModifyByFirstname(String firstname, Update update);
@Query("{ 'age' : null }")
Person findByQueryWithNullEqualityCheck();

6
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java

@ -28,6 +28,7 @@ import org.springframework.data.geo.Point; @@ -28,6 +28,7 @@ import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.convert.MongoWriter;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.TextCriteria;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.lang.Nullable;
@ -172,4 +173,9 @@ class StubParameterAccessor implements MongoParameterAccessor { @@ -172,4 +173,9 @@ class StubParameterAccessor implements MongoParameterAccessor {
public Class<?> findDynamicProjection() {
return null;
}
@Override
public Update getUpdate() {
return null;
}
}

Loading…
Cancel
Save