From 98d655f4e28c3f1a4ac8d5505bc330c5a6ed565c Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Mon, 6 Mar 2017 16:19:11 +0100 Subject: [PATCH] DATAMONGO-1639 - Make sure BeforeConvertEvent sees new version for updates. The changes for DATAMONGO-1617 subtley changed the behavior for entity updates in terms of the version value they see for entities using optimistic locking. Previously the updates already saw the new version value, where after the fix for DATAMONGO-1617 it saw the old one. That caused BeforeConvertEvent listeners not being able to distinguish between an original insert and the first update anymore. This change is now rolled back and we introduced a test case that encodes this expectation explicitly. --- .../data/mongodb/core/MongoTemplate.java | 14 ++++---- .../mongodb/core/MongoTemplateUnitTests.java | 33 +++++++++++++++++++ 2 files changed, 40 insertions(+), 7 deletions(-) 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 59b79664c..b1d32978f 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 @@ -997,16 +997,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, doInsert(collectionName, objectToSave, this.mongoConverter); } else { - maybeEmitEvent(new BeforeConvertEvent(objectToSave, collectionName)); - assertUpdateableIdIfNotSet(objectToSave); - - // Create query for entity with the id and old version - Object id = convertingAccessor.getProperty(idProperty); - Query query = new Query(Criteria.where(idProperty.getName()).is(id).and(versionProperty.getName()).is(version)); - // Bump version number convertingAccessor.setProperty(versionProperty, versionNumber.longValue() + 1); + maybeEmitEvent(new BeforeConvertEvent(objectToSave, collectionName)); + assertUpdateableIdIfNotSet(objectToSave); + Document document = new Document(); this.mongoConverter.write(objectToSave, document); @@ -1014,6 +1010,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, maybeEmitEvent(new BeforeSaveEvent(objectToSave, document, collectionName)); Update update = Update.fromDocument(document, ID_FIELD); + // Create query for entity with the id and old version + Object id = convertingAccessor.getProperty(idProperty); + Query query = new Query(Criteria.where(idProperty.getName()).is(id).and(versionProperty.getName()).is(version)); + UpdateResult result = doUpdate(collectionName, query, update, objectToSave.getClass(), false, false); if (result.getModifiedCount() == 0) { 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 501f02d19..857c17401 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 @@ -56,6 +56,8 @@ import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener; +import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent; import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions; import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.Criteria; @@ -75,6 +77,7 @@ import com.mongodb.client.MongoCursor; import com.mongodb.client.MongoDatabase; import com.mongodb.client.model.FindOneAndUpdateOptions; import com.mongodb.client.model.UpdateOptions; +import com.mongodb.client.result.UpdateResult; /** * Unit tests for {@link MongoTemplate}. @@ -498,6 +501,36 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { verify(output, times(1)).limit(1000); } + @Test // DATAMONGO-1639 + public void beforeConvertEventForUpdateSeesNextVersion() { + + final VersionedEntity entity = new VersionedEntity(); + entity.id = 1; + entity.version = 0; + + GenericApplicationContext context = new GenericApplicationContext(); + context.refresh(); + context.addApplicationListener(new AbstractMongoEventListener() { + + @Override + public void onBeforeConvert(BeforeConvertEvent event) { + assertThat(event.getSource().version, is(1)); + } + }); + + template.setApplicationContext(context); + + MongoTemplate spy = Mockito.spy(template); + + UpdateResult result = mock(UpdateResult.class); + doReturn(1L).when(result).getModifiedCount(); + + doReturn(result).when(spy).doUpdate(anyString(), Mockito.any(Query.class), Mockito.any(Update.class), + Mockito.any(Class.class), anyBoolean(), anyBoolean()); + + spy.save(entity); + } + class AutogenerateableId { @Id BigInteger id;