From 93f81f7321fd4de727fba9e25d5c20b8ef13fb4f 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 | 30 +++++++++++++++++++ 2 files changed, 37 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 1f3fd76fe..6cd158820 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 @@ -1001,16 +1001,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); + BasicDBObject dbObject = new BasicDBObject(); this.mongoConverter.write(objectToSave, dbObject); @@ -1018,6 +1014,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { maybeEmitEvent(new BeforeSaveEvent(objectToSave, dbObject, collectionName)); Update update = Update.fromDBObject(dbObject, 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)); + doUpdate(collectionName, query, update, objectToSave.getClass(), false, false); maybeEmitEvent(new AfterSaveEvent(objectToSave, dbObject, collectionName)); } 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 a4bc0595c..dcb8b9702 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 @@ -52,6 +52,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; @@ -72,6 +74,7 @@ import com.mongodb.MapReduceOutput; import com.mongodb.Mongo; import com.mongodb.MongoException; import com.mongodb.ReadPreference; +import com.mongodb.WriteResult; /** * Unit tests for {@link MongoTemplate}. @@ -465,6 +468,33 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { assertThat(captor.getValue().getLimit(), is(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); + + doReturn(mock(WriteResult.class)).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;