Skip to content

Commit ba2ab18

Browse files
mp911deodrotbohm
authored andcommitted
DATAMONGO-1992 - Add mutation support for immutable objects through ReactiveMongoTemplate.
Persisting methods of ReactiveMongoTemplate now return potentially new object instances of immutable objects. New instances are created using wither methods/Kotlin copy(…) methods if an immutable object requires association with an Id or the version number needs to be incremented.
1 parent 1eab66a commit ba2ab18

File tree

3 files changed

+106
-76
lines changed

3 files changed

+106
-76
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java

+8-8
Original file line numberDiff line numberDiff line change
@@ -927,7 +927,7 @@ <S, T> Mono<T> findAndReplace(Query query, S replacement, FindAndReplaceOptions
927927
* Insert is used to initially store the object into the database. To update an existing object use the save method.
928928
*
929929
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
930-
* @return the saved object.
930+
* @return the inserted object.
931931
*/
932932
<T> Mono<T> insert(T objectToSave);
933933

@@ -941,7 +941,7 @@ <S, T> Mono<T> findAndReplace(Query query, S replacement, FindAndReplaceOptions
941941
*
942942
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
943943
* @param collectionName name of the collection to store the object in. Must not be {@literal null}.
944-
* @return the saved object.
944+
* @return the inserted object.
945945
*/
946946
<T> Mono<T> insert(T objectToSave, String collectionName);
947947

@@ -950,7 +950,7 @@ <S, T> Mono<T> findAndReplace(Query query, S replacement, FindAndReplaceOptions
950950
*
951951
* @param batchToSave the batch of objects to save. Must not be {@literal null}.
952952
* @param entityClass class that determines the collection to use. Must not be {@literal null}.
953-
* @return the saved objects.
953+
* @return the inserted objects .
954954
*/
955955
<T> Flux<T> insert(Collection<? extends T> batchToSave, Class<?> entityClass);
956956

@@ -959,7 +959,7 @@ <S, T> Mono<T> findAndReplace(Query query, S replacement, FindAndReplaceOptions
959959
*
960960
* @param batchToSave the list of objects to save. Must not be {@literal null}.
961961
* @param collectionName name of the collection to store the object in. Must not be {@literal null}.
962-
* @return the saved objects.
962+
* @return the inserted objects.
963963
*/
964964
<T> Flux<T> insert(Collection<? extends T> batchToSave, String collectionName);
965965

@@ -987,7 +987,7 @@ <S, T> Mono<T> findAndReplace(Query query, S replacement, FindAndReplaceOptions
987987
* Insert is used to initially store the object into the database. To update an existing object use the save method.
988988
*
989989
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
990-
* @return the saved object.
990+
* @return the inserted objects.
991991
*/
992992
<T> Mono<T> insert(Mono<? extends T> objectToSave);
993993

@@ -996,7 +996,7 @@ <S, T> Mono<T> findAndReplace(Query query, S replacement, FindAndReplaceOptions
996996
*
997997
* @param batchToSave the publisher which provides objects to save. Must not be {@literal null}.
998998
* @param entityClass class that determines the collection to use. Must not be {@literal null}.
999-
* @return the saved objects.
999+
* @return the inserted objects.
10001000
*/
10011001
<T> Flux<T> insertAll(Mono<? extends Collection<? extends T>> batchToSave, Class<?> entityClass);
10021002

@@ -1005,7 +1005,7 @@ <S, T> Mono<T> findAndReplace(Query query, S replacement, FindAndReplaceOptions
10051005
*
10061006
* @param batchToSave the publisher which provides objects to save. Must not be {@literal null}.
10071007
* @param collectionName name of the collection to store the object in. Must not be {@literal null}.
1008-
* @return the saved objects.
1008+
* @return the inserted objects.
10091009
*/
10101010
<T> Flux<T> insertAll(Mono<? extends Collection<? extends T>> batchToSave, String collectionName);
10111011

@@ -1014,7 +1014,7 @@ <S, T> Mono<T> findAndReplace(Query query, S replacement, FindAndReplaceOptions
10141014
* class.
10151015
*
10161016
* @param objectsToSave the publisher which provides objects to save. Must not be {@literal null}.
1017-
* @return the saved objects.
1017+
* @return the inserted objects.
10181018
*/
10191019
<T> Flux<T> insertAll(Mono<? extends Collection<? extends T>> objectsToSave);
10201020

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java

+42-35
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@
144144
import com.mongodb.reactivestreams.client.MongoDatabase;
145145
import com.mongodb.reactivestreams.client.Success;
146146
import com.mongodb.util.JSONParseException;
147+
import reactor.util.function.Tuples;
147148

148149
/**
149150
* Primary implementation of {@link ReactiveMongoOperations}. It simplifies the use of Reactive MongoDB usage and helps
@@ -1252,17 +1253,18 @@ protected <T> Mono<T> doInsert(String collectionName, T objectToSave, MongoWrite
12521253

12531254
return Mono.defer(() -> {
12541255

1255-
initializeVersionProperty(objectToSave);
1256-
maybeEmitEvent(new BeforeConvertEvent<>(objectToSave, collectionName));
1256+
T toSave = (T) initializeVersionProperty(objectToSave);
1257+
maybeEmitEvent(new BeforeConvertEvent<>(toSave, collectionName));
12571258

1258-
Document dbDoc = toDocument(objectToSave, writer);
1259+
Document dbDoc = toDocument(toSave, writer);
12591260

1260-
maybeEmitEvent(new BeforeSaveEvent<>(objectToSave, dbDoc, collectionName));
1261+
maybeEmitEvent(new BeforeSaveEvent<>(toSave, dbDoc, collectionName));
12611262

1262-
Mono<T> afterInsert = insertDBObject(collectionName, dbDoc, objectToSave.getClass()).flatMap(id -> {
1263-
populateIdIfNecessary(objectToSave, id);
1264-
maybeEmitEvent(new AfterSaveEvent<>(objectToSave, dbDoc, collectionName));
1265-
return Mono.just(objectToSave);
1263+
Mono<T> afterInsert = insertDBObject(collectionName, dbDoc, toSave.getClass()).map(id -> {
1264+
1265+
T saved = (T) populateIdIfNecessary(toSave, id);
1266+
maybeEmitEvent(new AfterSaveEvent<>(saved, dbDoc, collectionName));
1267+
return saved;
12661268
});
12671269

12681270
return afterInsert;
@@ -1326,18 +1328,15 @@ protected <T> Flux<T> doInsertBatch(final String collectionName, final Collectio
13261328
Assert.notNull(writer, "MongoWriter must not be null!");
13271329

13281330
Mono<List<Tuple2<T, Document>>> prepareDocuments = Flux.fromIterable(batchToSave)
1329-
.flatMap(new Function<T, Flux<Tuple2<T, Document>>>() {
1330-
@Override
1331-
public Flux<Tuple2<T, Document>> apply(T o) {
1331+
.map(o -> {
13321332

1333-
initializeVersionProperty(o);
1334-
maybeEmitEvent(new BeforeConvertEvent<>(o, collectionName));
1333+
T toSave = (T) initializeVersionProperty(o);
1334+
maybeEmitEvent(new BeforeConvertEvent<>(toSave, collectionName));
13351335

1336-
Document dbDoc = toDocument(o, writer);
1336+
Document dbDoc = toDocument(toSave, writer);
13371337

1338-
maybeEmitEvent(new BeforeSaveEvent<>(o, dbDoc, collectionName));
1339-
return Flux.zip(Mono.just(o), Mono.just(dbDoc));
1340-
}
1338+
maybeEmitEvent(new BeforeSaveEvent<>(toSave, dbDoc, collectionName));
1339+
return Tuples.of(toSave, dbDoc);
13411340
}).collectList();
13421341

13431342
Flux<Tuple2<T, Document>> insertDocuments = prepareDocuments.flatMapMany(tuples -> {
@@ -1349,9 +1348,9 @@ public Flux<Tuple2<T, Document>> apply(T o) {
13491348

13501349
return insertDocuments.map(tuple -> {
13511350

1352-
populateIdIfNecessary(tuple.getT1(), tuple.getT2().get(ID_FIELD));
1353-
maybeEmitEvent(new AfterSaveEvent<>(tuple.getT1(), tuple.getT2(), collectionName));
1354-
return tuple.getT1();
1351+
T saved = (T) populateIdIfNecessary(tuple.getT1(), tuple.getT2().get(ID_FIELD));
1352+
maybeEmitEvent(new AfterSaveEvent<>(saved, tuple.getT2(), collectionName));
1353+
return saved;
13551354
});
13561355
}
13571356

@@ -1438,17 +1437,19 @@ private <T> Mono<T> doSaveVersioned(T objectToSave, MongoPersistentEntity<?> ent
14381437
// Bump version number
14391438
convertingAccessor.setProperty(versionProperty, versionNumber.longValue() + 1);
14401439

1441-
ReactiveMongoTemplate.this.maybeEmitEvent(new BeforeConvertEvent<>(objectToSave, collectionName));
1440+
T toSave = (T) convertingAccessor.getBean();
14421441

1443-
Document document = ReactiveMongoTemplate.this.toDocument(objectToSave, mongoConverter);
1442+
ReactiveMongoTemplate.this.maybeEmitEvent(new BeforeConvertEvent<T>(toSave, collectionName));
14441443

1445-
ReactiveMongoTemplate.this.maybeEmitEvent(new BeforeSaveEvent<>(objectToSave, document, collectionName));
1444+
Document document = ReactiveMongoTemplate.this.toDocument(toSave, mongoConverter);
1445+
1446+
ReactiveMongoTemplate.this.maybeEmitEvent(new BeforeSaveEvent<>(toSave, document, collectionName));
14461447
Update update = Update.fromDocument(document, ID_FIELD);
14471448

1448-
return doUpdate(collectionName, query, update, objectToSave.getClass(), false, false).map(updateResult -> {
1449+
return doUpdate(collectionName, query, update, toSave.getClass(), false, false).map(updateResult -> {
14491450

1450-
maybeEmitEvent(new AfterSaveEvent<>(objectToSave, document, collectionName));
1451-
return objectToSave;
1451+
maybeEmitEvent(new AfterSaveEvent<>(toSave, document, collectionName));
1452+
return toSave;
14521453
});
14531454
});
14541455
}
@@ -1465,9 +1466,9 @@ protected <T> Mono<T> doSave(String collectionName, T objectToSave, MongoWriter<
14651466

14661467
return saveDocument(collectionName, dbDoc, objectToSave.getClass()).map(id -> {
14671468

1468-
populateIdIfNecessary(objectToSave, id);
1469-
maybeEmitEvent(new AfterSaveEvent<>(objectToSave, dbDoc, collectionName));
1470-
return objectToSave;
1469+
T saved = (T) populateIdIfNecessary(objectToSave, id);
1470+
maybeEmitEvent(new AfterSaveEvent<>(saved, dbDoc, collectionName));
1471+
return saved;
14711472
});
14721473
});
14731474
}
@@ -2521,35 +2522,37 @@ protected <T> void maybeEmitEvent(MongoMappingEvent<T> event) {
25212522
* @param id
25222523
*/
25232524
@SuppressWarnings("unchecked")
2524-
private void populateIdIfNecessary(Object savedObject, @Nullable Object id) {
2525+
private Object populateIdIfNecessary(Object savedObject, @Nullable Object id) {
25252526

25262527
if (id == null) {
2527-
return;
2528+
return null;
25282529
}
25292530

25302531
if (savedObject instanceof Map) {
25312532

25322533
Map<String, Object> map = (Map<String, Object>) savedObject;
25332534
map.put(ID_FIELD, id);
25342535

2535-
return;
2536+
return map;
25362537
}
25372538

25382539
MongoPersistentProperty idProp = getIdPropertyFor(savedObject.getClass());
25392540

25402541
if (idProp == null) {
2541-
return;
2542+
return savedObject;
25422543
}
25432544

25442545
ConversionService conversionService = mongoConverter.getConversionService();
25452546
MongoPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(savedObject.getClass());
25462547
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(savedObject);
25472548

25482549
if (accessor.getProperty(idProp) != null) {
2549-
return;
2550+
return accessor.getBean();
25502551
}
25512552

25522553
new ConvertingPropertyAccessor(accessor, conversionService).setProperty(idProp, id);
2554+
2555+
return accessor.getBean();
25532556
}
25542557

25552558
private MongoCollection<Document> getAndPrepareCollection(MongoDatabase db, String collectionName) {
@@ -2807,15 +2810,19 @@ private <T> Document toDocument(T objectToSave, MongoWriter<T> writer) {
28072810
}
28082811
}
28092812

2810-
private void initializeVersionProperty(Object entity) {
2813+
private Object initializeVersionProperty(Object entity) {
28112814

28122815
MongoPersistentEntity<?> mongoPersistentEntity = getPersistentEntity(entity.getClass());
28132816

28142817
if (mongoPersistentEntity != null && mongoPersistentEntity.hasVersionProperty()) {
28152818
ConvertingPropertyAccessor accessor = new ConvertingPropertyAccessor(
28162819
mongoPersistentEntity.getPropertyAccessor(entity), mongoConverter.getConversionService());
28172820
accessor.setProperty(mongoPersistentEntity.getRequiredVersionProperty(), 0);
2821+
2822+
return accessor.getBean();
28182823
}
2824+
2825+
return entity;
28192826
}
28202827

28212828
// Callback implementations

0 commit comments

Comments
 (0)