From a759dff5fd3a8fbeb5859320e6764fc158c0d94d Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 4 Dec 2018 15:05:58 +0100 Subject: [PATCH] DATAMONGO-2155 - Introduce UpdateDefinition. Original pull request: #625. --- .../data/mongodb/core/MappedDocument.java | 43 ++++++++++++++- .../data/mongodb/core/MongoTemplate.java | 7 ++- .../mongodb/core/ReactiveMongoTemplate.java | 8 ++- .../data/mongodb/core/query/Update.java | 21 +++++-- .../mongodb/core/query/UpdateDefinition.java | 55 +++++++++++++++++++ .../mongodb/core/MongoTemplateUnitTests.java | 12 +--- 6 files changed, 125 insertions(+), 21 deletions(-) create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/UpdateDefinition.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappedDocument.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappedDocument.java index cb7eac8ed..df7bda92a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappedDocument.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappedDocument.java @@ -24,6 +24,7 @@ import java.util.List; import org.bson.Document; import org.bson.conversions.Bson; import org.springframework.data.mongodb.core.query.Update; +import org.springframework.data.mongodb.core.query.UpdateDefinition; import org.springframework.data.util.StreamUtils; import com.mongodb.client.model.Filters; @@ -86,17 +87,55 @@ public class MappedDocument { return new MappedUpdate(Update.fromDocument(document, ID_FIELD)); } - public class MappedUpdate extends Update { + /** + * An {@link UpdateDefinition} that indicates that the {@link #getUpdateObject() update object} has already been + * mapped to the specific domain type. + * + * @author Christoph Strobl + * @since 2.2 + */ + public class MappedUpdate implements UpdateDefinition { private final Update delegate; - public MappedUpdate(Update delegate) { + MappedUpdate(Update delegate) { this.delegate = delegate; } + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.UpdateDefinition#getUpdateObject() + */ @Override public Document getUpdateObject() { return delegate.getUpdateObject(); } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.UpdateDefinition#modifies(java.lang.String) + */ + @Override + public boolean modifies(String key) { + return delegate.modifies(key); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.UpdateDefinition#incVersion(java.lang.String) + */ + @Override + public void incVersion(String version) { + delegate.incVersion(version); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.UpdateDefinition#isIsolated() + */ + @Override + public Boolean isIsolated() { + return delegate.isIsolated(); + } } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index f7ccc4eb1..76762c197 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -94,6 +94,7 @@ import org.springframework.data.mongodb.core.query.Meta; import org.springframework.data.mongodb.core.query.NearQuery; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; +import org.springframework.data.mongodb.core.query.UpdateDefinition; import org.springframework.data.mongodb.core.validation.Validator; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.util.CloseableIterator; @@ -1555,7 +1556,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, return doUpdate(collectionName, query, update, entityClass, false, true); } - protected UpdateResult doUpdate(final String collectionName, final Query query, final Update update, + protected UpdateResult doUpdate(final String collectionName, final Query query, final UpdateDefinition update, @Nullable final Class entityClass, final boolean upsert, final boolean multi) { Assert.notNull(collectionName, "CollectionName must not be null!"); @@ -1616,12 +1617,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, }); } - private void increaseVersionForUpdateIfNecessary(@Nullable MongoPersistentEntity persistentEntity, Update update) { + private void increaseVersionForUpdateIfNecessary(@Nullable MongoPersistentEntity persistentEntity, UpdateDefinition update) { if (persistentEntity != null && persistentEntity.hasVersionProperty()) { String versionFieldName = persistentEntity.getRequiredVersionProperty().getFieldName(); if (!update.modifies(versionFieldName)) { - update.inc(versionFieldName, 1L); + update.incVersion(versionFieldName); } } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java index 30b0ded61..544ce4f24 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java @@ -92,6 +92,7 @@ import org.springframework.data.mongodb.core.query.Meta; import org.springframework.data.mongodb.core.query.NearQuery; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; +import org.springframework.data.mongodb.core.query.UpdateDefinition; import org.springframework.data.mongodb.core.validation.Validator; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.util.Optionals; @@ -1611,7 +1612,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati return doUpdate(collectionName, query, update, entityClass, false, true); } - protected Mono doUpdate(String collectionName, Query query, @Nullable Update update, + protected Mono doUpdate(String collectionName, Query query, @Nullable UpdateDefinition update, @Nullable Class entityClass, boolean upsert, boolean multi) { MongoPersistentEntity entity = entityClass == null ? null : getPersistentEntity(entityClass); @@ -1668,12 +1669,13 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati return result.next(); } - private void increaseVersionForUpdateIfNecessary(@Nullable MongoPersistentEntity persistentEntity, Update update) { + private void increaseVersionForUpdateIfNecessary(@Nullable MongoPersistentEntity persistentEntity, + UpdateDefinition update) { if (persistentEntity != null && persistentEntity.hasVersionProperty()) { String versionFieldName = persistentEntity.getRequiredVersionProperty().getFieldName(); if (!update.modifies(versionFieldName)) { - update.inc(versionFieldName, 1L); + update.incVersion(versionFieldName); } } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java index c4c793221..c972105d5 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java @@ -48,7 +48,7 @@ import org.springframework.util.StringUtils; * @author Mark Paluch * @author Pavel Vodrazka */ -public class Update { +public class Update implements UpdateDefinition { public enum Position { LAST, FIRST @@ -155,6 +155,15 @@ public class Update { return this; } + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.UpdateDefinition#incVersion() + */ + @Override + public void incVersion(String key) { + inc(key, 1L); + } + /** * Update using the {@literal $push} update modifier * @@ -390,14 +399,18 @@ public class Update { return this; } - /** - * @return {@literal true} if update isolated is set. - * @since 2.0 + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.UpdateDefinition#isIsolated() */ public Boolean isIsolated() { return isolated; } + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.UpdateDefinition#getUpdateObject() + */ public Document getUpdateObject() { return new Document(modifierOps); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/UpdateDefinition.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/UpdateDefinition.java new file mode 100644 index 000000000..9e3240dce --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/UpdateDefinition.java @@ -0,0 +1,55 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.query; + +import org.bson.Document; + +/** + * Interface fixing must have operations for {@literal updates} as implemented via {@link Update}. + * + * @author Christoph Strobl + * @since 2.2 + */ +public interface UpdateDefinition { + + /** + * If {@literal true} prevents a write operation that affects multiple documents from yielding to + * other reads or writes once the first document is written.
+ * + * @return {@literal true} if update isolated is set. + */ + Boolean isIsolated(); + + /** + * @return the actual update in its native {@link Document} format. Never {@literal null}. + */ + Document getUpdateObject(); + + /** + * Check if a given {@literal key} is modified by applying the update. + * + * @param key must not be {@literal null}. + * @return {@literal true} if the actual {@link UpdateDefinition} attempts to modify the given {@literal key}. + */ + boolean modifies(String key); + + /** + * Bump the version of a given {@literal key} by {@code 1}. + * + * @param key must not be {@literal null}. + */ + void incVersion(String key); +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java index 71da4c9c3..480dbebc0 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java @@ -537,6 +537,8 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { @Test // DATAMONGO-1639 public void beforeConvertEventForUpdateSeesNextVersion() { + when(updateResult.getModifiedCount()).thenReturn(1L); + final VersionedEntity entity = new VersionedEntity(); entity.id = 1; entity.version = 0; @@ -553,15 +555,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { template.setApplicationContext(context); - MongoTemplate spy = Mockito.spy(template); - - UpdateResult result = mock(UpdateResult.class); - doReturn(1L).when(result).getModifiedCount(); - - doReturn(result).when(spy).doUpdate(anyString(), any(Query.class), any(Update.class), any(Class.class), - anyBoolean(), anyBoolean()); - - spy.save(entity); + template.save(entity); } @Test // DATAMONGO-1447