diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperation.java index 8a9e0411d..4d8672b2e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperation.java @@ -63,17 +63,19 @@ public interface ExecutableInsertOperation { * Insert exactly one object. * * @param object must not be {@literal null}. + * @return the inserted object. * @throws IllegalArgumentException if object is {@literal null}. */ - void one(T object); + T one(T object); /** * Insert a collection of objects. * * @param objects must not be {@literal null}. + * @return the inserted objects. * @throws IllegalArgumentException if objects is {@literal null}. */ - void all(Collection objects); + Collection all(Collection objects); } /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperationSupport.java index 512af9efb..ee75ad11c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperationSupport.java @@ -72,11 +72,11 @@ class ExecutableInsertOperationSupport implements ExecutableInsertOperation { * @see org.springframework.data.mongodb.core.ExecutableInsertOperation.TerminatingInsert#insert(java.lang.Class) */ @Override - public void one(T object) { + public T one(T object) { Assert.notNull(object, "Object must not be null!"); - template.insert(object, getCollectionName()); + return template.insert(object, getCollectionName()); } /* @@ -84,11 +84,11 @@ class ExecutableInsertOperationSupport implements ExecutableInsertOperation { * @see org.springframework.data.mongodb.core.ExecutableInsertOperation.TerminatingInsert#all(java.util.Collection) */ @Override - public void all(Collection objects) { + public Collection all(Collection objects) { Assert.notNull(objects, "Objects must not be null!"); - template.insert(objects, getCollectionName()); + return template.insert(objects, getCollectionName()); } /* diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java index ece8ab3d0..85dbaf347 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java @@ -1141,8 +1141,9 @@ public interface MongoOperations extends FluentMongoOperations { * Insert is used to initially store the object into the database. To update an existing object use the save method. * * @param objectToSave the object to store in the collection. Must not be {@literal null}. + * @return the inserted object. */ - void insert(Object objectToSave); + T insert(T objectToSave); /** * Insert the object into the specified collection. @@ -1154,32 +1155,36 @@ public interface MongoOperations extends FluentMongoOperations { * * @param objectToSave the object to store in the collection. Must not be {@literal null}. * @param collectionName name of the collection to store the object in. Must not be {@literal null}. + * @return the inserted object. */ - void insert(Object objectToSave, String collectionName); + T insert(T objectToSave, String collectionName); /** * Insert a Collection of objects into a collection in a single batch write to the database. * * @param batchToSave the batch of objects to save. Must not be {@literal null}. * @param entityClass class that determines the collection to use. Must not be {@literal null}. + * @return the inserted objects that. */ - void insert(Collection batchToSave, Class entityClass); + Collection insert(Collection batchToSave, Class entityClass); /** * Insert a batch of objects into the specified collection in a single batch write to the database. * * @param batchToSave the list of objects to save. Must not be {@literal null}. * @param collectionName name of the collection to store the object in. Must not be {@literal null}. + * @return the inserted objects that. */ - void insert(Collection batchToSave, String collectionName); + Collection insert(Collection batchToSave, String collectionName); /** * Insert a mixed Collection of objects into a database collection determining the collection name to use based on the * class. * * @param objectsToSave the list of objects to save. Must not be {@literal null}. + * @return the inserted objects. */ - void insertAll(Collection objectsToSave); + Collection insertAll(Collection objectsToSave); /** * Save the object to the collection for the entity type of the object to save. This will perform an insert if the @@ -1195,8 +1200,9 @@ public interface MongoOperations extends FluentMongoOperations { * Conversion" for more details. * * @param objectToSave the object to store in the collection. Must not be {@literal null}. + * @return the saved object. */ - void save(Object objectToSave); + T save(T objectToSave); /** * Save the object to the specified collection. This will perform an insert if the object is not already present, that @@ -1213,8 +1219,9 @@ public interface MongoOperations extends FluentMongoOperations { * * @param objectToSave the object to store in the collection. Must not be {@literal null}. * @param collectionName name of the collection to store the object in. Must not be {@literal null}. + * @return the saved object. */ - void save(Object objectToSave, String collectionName); + T save(T objectToSave, String collectionName); /** * Performs an upsert. If no document is found that matches the query, a new document is created and inserted by 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 f27602ec6..011d6845b 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 @@ -73,16 +73,7 @@ import org.springframework.data.mongodb.core.aggregation.AggregationResults; import org.springframework.data.mongodb.core.aggregation.Fields; import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext; import org.springframework.data.mongodb.core.aggregation.TypedAggregation; -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; -import org.springframework.data.mongodb.core.convert.JsonSchemaMapper; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.convert.MongoCustomConversions; -import org.springframework.data.mongodb.core.convert.MongoJsonSchemaMapper; -import org.springframework.data.mongodb.core.convert.MongoWriter; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.convert.UpdateMapper; +import org.springframework.data.mongodb.core.convert.*; import org.springframework.data.mongodb.core.index.IndexOperations; import org.springframework.data.mongodb.core.index.IndexOperationsProvider; import org.springframework.data.mongodb.core.index.MongoMappingEventPublisher; @@ -1157,12 +1148,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, * @see org.springframework.data.mongodb.core.MongoOperations#insert(java.lang.Object) */ @Override - public void insert(Object objectToSave) { + public T insert(T objectToSave) { Assert.notNull(objectToSave, "ObjectToSave must not be null!"); ensureNotIterable(objectToSave); - insert(objectToSave, determineEntityCollectionName(objectToSave)); + return insert(objectToSave, determineEntityCollectionName(objectToSave)); } /* @@ -1170,13 +1161,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, * @see org.springframework.data.mongodb.core.MongoOperations#insert(java.lang.Object, java.lang.String) */ @Override - public void insert(Object objectToSave, String collectionName) { + public T insert(T objectToSave, String collectionName) { Assert.notNull(objectToSave, "ObjectToSave must not be null!"); Assert.notNull(collectionName, "CollectionName must not be null!"); ensureNotIterable(objectToSave); - doInsert(collectionName, objectToSave, this.mongoConverter); + return (T) doInsert(collectionName, objectToSave, this.mongoConverter); } protected void ensureNotIterable(@Nullable Object o) { @@ -1230,19 +1221,21 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, return wc; } - protected void doInsert(String collectionName, T objectToSave, MongoWriter writer) { + protected T doInsert(String collectionName, T objectToSave, MongoWriter writer) { - initializeVersionProperty(objectToSave); - maybeEmitEvent(new BeforeConvertEvent<>(objectToSave, collectionName)); - assertUpdateableIdIfNotSet(objectToSave); + T toSave = (T) initializeVersionProperty(objectToSave); + maybeEmitEvent(new BeforeConvertEvent<>(toSave, collectionName)); + assertUpdateableIdIfNotSet(toSave); - Document dbDoc = toDocument(objectToSave, writer); + Document dbDoc = toDocument(toSave, writer); - maybeEmitEvent(new BeforeSaveEvent<>(objectToSave, dbDoc, collectionName)); - Object id = insertDocument(collectionName, dbDoc, objectToSave.getClass()); + maybeEmitEvent(new BeforeSaveEvent<>(toSave, dbDoc, collectionName)); + Object id = insertDocument(collectionName, dbDoc, toSave.getClass()); - populateIdIfNecessary(objectToSave, id); - maybeEmitEvent(new AfterSaveEvent<>(objectToSave, dbDoc, collectionName)); + T saved = (T) populateIdIfNecessary(toSave, id); + maybeEmitEvent(new AfterSaveEvent<>(saved, dbDoc, collectionName)); + + return saved; } /** @@ -1275,7 +1268,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } } - private void initializeVersionProperty(Object entity) { + private Object initializeVersionProperty(Object entity) { MongoPersistentEntity persistentEntity = getPersistentEntity(entity.getClass()); @@ -1286,36 +1279,41 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, ConvertingPropertyAccessor accessor = new ConvertingPropertyAccessor(persistentEntity.getPropertyAccessor(entity), mongoConverter.getConversionService()); accessor.setProperty(versionProperty, 0); + + return accessor.getBean(); } + + return entity; } @Override - public void insert(Collection batchToSave, Class entityClass) { + public Collection insert(Collection batchToSave, Class entityClass) { Assert.notNull(batchToSave, "BatchToSave must not be null!"); - doInsertBatch(determineCollectionName(entityClass), batchToSave, this.mongoConverter); + return (Collection) doInsertBatch(determineCollectionName(entityClass), batchToSave, this.mongoConverter); } @Override - public void insert(Collection batchToSave, String collectionName) { + public Collection insert(Collection batchToSave, String collectionName) { Assert.notNull(batchToSave, "BatchToSave must not be null!"); Assert.notNull(collectionName, "CollectionName must not be null!"); - doInsertBatch(collectionName, batchToSave, this.mongoConverter); + return (Collection) doInsertBatch(collectionName, batchToSave, this.mongoConverter); } @Override - public void insertAll(Collection objectsToSave) { + public Collection insertAll(Collection objectsToSave) { Assert.notNull(objectsToSave, "ObjectsToSave must not be null!"); - doInsertAll(objectsToSave, this.mongoConverter); + return (Collection) doInsertAll(objectsToSave, this.mongoConverter); } - protected void doInsertAll(Collection listToSave, MongoWriter writer) { + protected Collection doInsertAll(Collection listToSave, MongoWriter writer) { Map> elementsByCollection = new HashMap<>(); + List savedObjects = new ArrayList<>(listToSave.size()); for (T element : listToSave) { @@ -1337,47 +1335,59 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } for (Map.Entry> entry : elementsByCollection.entrySet()) { - doInsertBatch(entry.getKey(), entry.getValue(), this.mongoConverter); + savedObjects.addAll((Collection) doInsertBatch(entry.getKey(), entry.getValue(), this.mongoConverter)); } + + return savedObjects; } - protected void doInsertBatch(String collectionName, Collection batchToSave, MongoWriter writer) { + protected Collection doInsertBatch(String collectionName, Collection batchToSave, + MongoWriter writer) { Assert.notNull(writer, "MongoWriter must not be null!"); List documentList = new ArrayList<>(); - for (T o : batchToSave) { + List initializedBatchToSave = new ArrayList<>(batchToSave.size()); + for (T uninitialized : batchToSave) { - initializeVersionProperty(o); - maybeEmitEvent(new BeforeConvertEvent<>(o, collectionName)); + T toSave = (T) initializeVersionProperty(uninitialized); + maybeEmitEvent(new BeforeConvertEvent<>(toSave, collectionName)); - Document document = toDocument(o, writer); + Document document = toDocument(toSave, writer); - maybeEmitEvent(new BeforeSaveEvent<>(o, document, collectionName)); + maybeEmitEvent(new BeforeSaveEvent<>(toSave, document, collectionName)); documentList.add(document); + initializedBatchToSave.add(toSave); } List ids = insertDocumentList(collectionName, documentList); + List savedObjects = new ArrayList<>(documentList.size()); int i = 0; - for (T obj : batchToSave) { + for (T obj : initializedBatchToSave) { + if (i < ids.size()) { - populateIdIfNecessary(obj, ids.get(i)); - maybeEmitEvent(new AfterSaveEvent<>(obj, documentList.get(i), collectionName)); + T saved = (T) populateIdIfNecessary(obj, ids.get(i)); + maybeEmitEvent(new AfterSaveEvent<>(saved, documentList.get(i), collectionName)); + savedObjects.add(saved); + } else { + savedObjects.add(obj); } i++; } + + return savedObjects; } @Override - public void save(Object objectToSave) { + public T save(T objectToSave) { Assert.notNull(objectToSave, "Object to save must not be null!"); - save(objectToSave, determineEntityCollectionName(objectToSave)); + return save(objectToSave, determineEntityCollectionName(objectToSave)); } @Override - public void save(Object objectToSave, String collectionName) { + public T save(T objectToSave, String collectionName) { Assert.notNull(objectToSave, "Object to save must not be null!"); Assert.hasText(collectionName, "Collection name must not be null or empty!"); @@ -1385,11 +1395,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, MongoPersistentEntity entity = getPersistentEntity(objectToSave.getClass()); if (entity != null && entity.hasVersionProperty()) { - doSaveVersioned(objectToSave, entity, collectionName); - return; + return doSaveVersioned(objectToSave, entity, collectionName); } - doSave(collectionName, objectToSave, this.mongoConverter); + return (T) doSave(collectionName, objectToSave, this.mongoConverter); } private T doSaveVersioned(T objectToSave, MongoPersistentEntity entity, String collectionName) { @@ -1398,42 +1407,43 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, entity.getPropertyAccessor(objectToSave), mongoConverter.getConversionService()); MongoPersistentProperty property = entity.getRequiredVersionProperty(); - Number number = convertingAccessor.getProperty(property, Number.class); + Number number = (Number) convertingAccessor.getProperty(property, Number.class); if (number != null) { // Bump version number convertingAccessor.setProperty(property, number.longValue() + 1); - maybeEmitEvent(new BeforeConvertEvent<>(objectToSave, collectionName)); - assertUpdateableIdIfNotSet(objectToSave); + T toSave = (T) convertingAccessor.getBean(); + + maybeEmitEvent(new BeforeConvertEvent<>(toSave, collectionName)); + assertUpdateableIdIfNotSet(toSave); Document document = new Document(); - this.mongoConverter.write(objectToSave, document); + this.mongoConverter.write(toSave, document); - maybeEmitEvent(new BeforeSaveEvent<>(objectToSave, document, collectionName)); + maybeEmitEvent(new BeforeSaveEvent<>(toSave, document, collectionName)); Update update = Update.fromDocument(document, ID_FIELD); // Create query for entity with the id and old version MongoPersistentProperty idProperty = entity.getRequiredIdProperty(); - Object id = entity.getIdentifierAccessor(objectToSave).getRequiredIdentifier(); + Object id = entity.getIdentifierAccessor(toSave).getRequiredIdentifier(); Query query = new Query(Criteria.where(idProperty.getName()).is(id).and(property.getName()).is(number)); - UpdateResult result = doUpdate(collectionName, query, update, objectToSave.getClass(), false, false); + UpdateResult result = doUpdate(collectionName, query, update, toSave.getClass(), false, false); if (result.getModifiedCount() == 0) { throw new OptimisticLockingFailureException( String.format("Cannot save entity %s with version %s to collection %s. Has it been modified meanwhile?", id, number, collectionName)); } - maybeEmitEvent(new AfterSaveEvent<>(objectToSave, document, collectionName)); + maybeEmitEvent(new AfterSaveEvent<>(toSave, document, collectionName)); - return objectToSave; + return toSave; } - doInsert(collectionName, objectToSave, this.mongoConverter); - return objectToSave; + return (T) doInsert(collectionName, objectToSave, this.mongoConverter); } protected T doSave(String collectionName, T objectToSave, MongoWriter writer) { @@ -1446,10 +1456,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, maybeEmitEvent(new BeforeSaveEvent<>(objectToSave, dbDoc, collectionName)); Object id = saveDocument(collectionName, dbDoc, objectToSave.getClass()); - populateIdIfNecessary(objectToSave, id); - maybeEmitEvent(new AfterSaveEvent<>(objectToSave, dbDoc, collectionName)); + T saved = (T) populateIdIfNecessary(objectToSave, id); + maybeEmitEvent(new AfterSaveEvent<>(saved, dbDoc, collectionName)); - return objectToSave; + return saved; } protected Object insertDocument(final String collectionName, final Document document, final Class entityClass) { @@ -2674,10 +2684,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, * @param id */ @SuppressWarnings("unchecked") - protected void populateIdIfNecessary(Object savedObject, Object id) { + protected Object populateIdIfNecessary(Object savedObject, Object id) { if (id == null) { - return; + return null; } if (savedObject instanceof Map) { @@ -2685,7 +2695,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, Map map = (Map) savedObject; map.put(ID_FIELD, id); - return; + return map; } MongoPersistentProperty idProperty = getIdPropertyFor(savedObject.getClass()); @@ -2700,7 +2710,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, if (value == null) { new ConvertingPropertyAccessor(accessor, conversionService).setProperty(idProperty, id); } + + return accessor.getBean(); } + + return savedObject; } private MongoCollection getAndPrepareCollection(MongoDatabase db, String collectionName) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java index e8c2a400f..39099a18b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java @@ -17,6 +17,7 @@ package org.springframework.data.mongodb.repository.support; import static org.springframework.data.mongodb.core.query.Criteria.*; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -77,12 +78,10 @@ public class SimpleMongoRepository implements MongoRepository { Assert.notNull(entity, "Entity must not be null!"); if (entityInformation.isNew(entity)) { - mongoOperations.insert(entity, entityInformation.getCollectionName()); - } else { - mongoOperations.save(entity, entityInformation.getCollectionName()); + return mongoOperations.insert(entity, entityInformation.getCollectionName()); } - return entity; + return mongoOperations.save(entity, entityInformation.getCollectionName()); } /* @@ -100,12 +99,10 @@ public class SimpleMongoRepository implements MongoRepository { if (allNew) { List result = source.stream().collect(Collectors.toList()); - mongoOperations.insert(result, entityInformation.getCollectionName()); - return result; - - } else { - return source.stream().map(this::save).collect(Collectors.toList()); + return new ArrayList<>(mongoOperations.insert(result, entityInformation.getCollectionName())); } + + return source.stream().map(this::save).collect(Collectors.toList()); } /* @@ -244,8 +241,7 @@ public class SimpleMongoRepository implements MongoRepository { Assert.notNull(entity, "Entity must not be null!"); - mongoOperations.insert(entity, entityInformation.getCollectionName()); - return entity; + return mongoOperations.insert(entity, entityInformation.getCollectionName()); } /* @@ -263,8 +259,7 @@ public class SimpleMongoRepository implements MongoRepository { return list; } - mongoOperations.insertAll(list); - return list; + return new ArrayList<>(mongoOperations.insertAll(list)); } /* diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java index b8e92d0d0..4edfcd4f9 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java @@ -39,6 +39,7 @@ import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; +import lombok.experimental.Wither; import org.bson.types.ObjectId; import org.hamcrest.collection.IsMapContaining; import org.joda.time.DateTime; @@ -1638,6 +1639,21 @@ public class MongoTemplateTests { assertThat(person.version, is(0)); } + @Test // DATAMONGO-1992 + public void initializesIdAndVersionAndOfImmutableObject() { + + ImmutableVersioned versioned = new ImmutableVersioned(); + + ImmutableVersioned saved = template.insert(versioned); + + assertThat(saved, is(not(sameInstance(versioned)))); + assertThat(versioned.id, is(nullValue())); + assertThat(versioned.version, is(nullValue())); + + assertThat(saved.id, is(notNullValue())); + assertThat(saved.version, is(0L)); + } + @Test(expected = IllegalArgumentException.class) // DATAMONGO-568, DATAMONGO-1762 public void queryCantBeNull() { template.find(null, PersonWithIdPropertyOfTypeObjectId.class); @@ -4057,4 +4073,17 @@ public class MongoTemplateTests { } } + + @AllArgsConstructor + @Wither + static class ImmutableVersioned { + + final @Id String id; + final @Version Long version; + + public ImmutableVersioned() { + id = null; + version = null; + } + } }