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.
This commit is contained in:
Oliver Gierke
2017-03-06 16:19:11 +01:00
parent 98a9b66e6b
commit 98d655f4e2
2 changed files with 40 additions and 7 deletions

View File

@@ -997,16 +997,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
doInsert(collectionName, objectToSave, this.mongoConverter);
} else {
maybeEmitEvent(new BeforeConvertEvent<T>(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<T>(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<T>(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) {

View File

@@ -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<VersionedEntity>() {
@Override
public void onBeforeConvert(BeforeConvertEvent<VersionedEntity> 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;