Skip to content

Commit 7500ba1

Browse files
committed
DATAMONGO-2195 - Throw OptimisticLockingFailureException on delete only in repositories.
OptimisticLockingFailureException is now thrown only when deleting an entity through a repository and no longer when using the Template API. Original pull request: #641.
1 parent 4ecf20c commit 7500ba1

File tree

14 files changed

+233
-248
lines changed

14 files changed

+233
-248
lines changed

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

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,36 +1730,10 @@ public DeleteResult doInCollection(MongoCollection<Document> collection)
17301730
DeleteResult result = multi ? collectionToUse.deleteMany(removeQuery, options)
17311731
: collectionToUse.deleteOne(removeQuery, options);
17321732

1733-
checkForOptimisticLockingFailures(result, removeQuery, collectionToUse);
1734-
17351733
maybeEmitEvent(new AfterDeleteEvent<>(queryObject, entityClass, collectionName));
17361734

17371735
return result;
17381736
}
1739-
1740-
private void checkForOptimisticLockingFailures(DeleteResult result, Document removeQuery,
1741-
MongoCollection<Document> collectionToUse) {
1742-
1743-
if (multi || !ResultOperations.isUndecidedDeleteResult(result, removeQuery, entity)) {
1744-
return;
1745-
}
1746-
1747-
String versionFieldName = entity.getVersionProperty().getFieldName();
1748-
Document idQuery = new Document(removeQuery);
1749-
idQuery.remove(versionFieldName);
1750-
1751-
Iterator<Document> it = collectionToUse.find(idQuery).projection(Projections.include("_id", versionFieldName))
1752-
.limit(1).iterator();
1753-
1754-
if (it.hasNext()) {
1755-
1756-
Document source = it.next();
1757-
throw ResultOperations.newDeleteVersionedOptimisticLockingException(source.get("_id"), collectionName,
1758-
removeQuery.get(versionFieldName), source.get(versionFieldName));
1759-
1760-
}
1761-
1762-
}
17631737
});
17641738
}
17651739

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

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1824,35 +1824,6 @@ protected <T> Mono<DeleteResult> doRemove(String collectionName, Query query, @N
18241824
return collectionToUse.deleteMany(removeQuery, deleteOptions);
18251825
}
18261826

1827-
}).flatMap(deleteResult -> {
1828-
1829-
if (!ResultOperations.isUndecidedDeleteResult(deleteResult, removeQuery, entity)) {
1830-
return Mono.just(deleteResult);
1831-
}
1832-
1833-
return execute(collectionName, (coll -> {
1834-
1835-
String versionFieldName = entity.getVersionProperty().getFieldName();
1836-
1837-
Document queryWithoutVersion = new Document(removeQuery);
1838-
queryWithoutVersion.remove(versionFieldName);
1839-
1840-
Publisher<Document> publisher = coll.find(queryWithoutVersion)
1841-
.projection(Projections.include("_id", entity.getVersionProperty().getFieldName())).limit(1).first();
1842-
1843-
return Mono.from(publisher) //
1844-
.defaultIfEmpty(new Document()) //
1845-
.flatMap(it -> {
1846-
1847-
if (it.isEmpty()) {
1848-
return Mono.just(deleteResult);
1849-
}
1850-
1851-
return Mono.error(ResultOperations.newDeleteVersionedOptimisticLockingException(it.get("_id"),
1852-
collectionName, removeQuery.get(versionFieldName), it.get(versionFieldName)));
1853-
});
1854-
})).next();
1855-
18561827
}).doOnNext(it -> maybeEmitEvent(new AfterDeleteEvent<>(queryObject, entityClass, collectionName))) //
18571828
.next();
18581829
}

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

Lines changed: 0 additions & 76 deletions
This file was deleted.

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoEntityInformation.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616
package org.springframework.data.mongodb.repository.query;
1717

1818
import org.springframework.data.repository.core.EntityInformation;
19+
import org.springframework.lang.Nullable;
1920

2021
/**
2122
* Mongo specific {@link EntityInformation}.
2223
*
2324
* @author Oliver Gierke
25+
* @author Mark Paluch
2426
*/
2527
public interface MongoEntityInformation<T, ID> extends EntityInformation<T, ID> {
2628

@@ -37,4 +39,25 @@ public interface MongoEntityInformation<T, ID> extends EntityInformation<T, ID>
3739
* @return
3840
*/
3941
String getIdAttribute();
42+
43+
/**
44+
* Returns whether the entity uses optimistic locking.
45+
*
46+
* @return
47+
* @since 2.2
48+
*/
49+
default boolean isVersioned() {
50+
return false;
51+
}
52+
53+
/**
54+
* Returns the version value for the entity or {@literal null} if the entity is not {@link #isVersioned() versioned}.
55+
*
56+
* @param entity must not be {@literal null}
57+
* @return
58+
*/
59+
@Nullable
60+
default Object getVersion(T entity) {
61+
return null;
62+
}
4063
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MappingMongoEntityInformation.java

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.data.mongodb.repository.support;
1717

1818
import org.bson.types.ObjectId;
19+
import org.springframework.data.mapping.PersistentPropertyAccessor;
1920
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
2021
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
2122
import org.springframework.data.repository.core.support.PersistentEntityInformation;
@@ -87,14 +88,16 @@ private MappingMongoEntityInformation(MongoPersistentEntity<T> entity, @Nullable
8788
this.fallbackIdType = idType != null ? idType : (Class<ID>) ObjectId.class;
8889
}
8990

90-
/* (non-Javadoc)
91+
/*
92+
* (non-Javadoc)
9193
* @see org.springframework.data.mongodb.repository.MongoEntityInformation#getCollectionName()
9294
*/
9395
public String getCollectionName() {
9496
return customCollectionName == null ? entityMetadata.getCollection() : customCollectionName;
9597
}
9698

97-
/* (non-Javadoc)
99+
/*
100+
* (non-Javadoc)
98101
* @see org.springframework.data.mongodb.repository.MongoEntityInformation#getIdAttribute()
99102
*/
100103
public String getIdAttribute() {
@@ -106,7 +109,6 @@ public String getIdAttribute() {
106109
* @see org.springframework.data.repository.core.support.PersistentEntityInformation#getIdType()
107110
*/
108111
@Override
109-
@SuppressWarnings("unchecked")
110112
public Class<ID> getIdType() {
111113

112114
if (this.entityMetadata.hasIdProperty()) {
@@ -115,4 +117,30 @@ public Class<ID> getIdType() {
115117

116118
return fallbackIdType;
117119
}
120+
121+
/*
122+
* (non-Javadoc)
123+
* @see org.springframework.data.mongodb.repository.MongoEntityInformation#isVersioned()
124+
*/
125+
@Override
126+
public boolean isVersioned() {
127+
return this.entityMetadata.hasVersionProperty();
128+
}
129+
130+
/*
131+
* (non-Javadoc)
132+
* @see org.springframework.data.mongodb.repository.MongoEntityInformation#getVersion(Object)
133+
*/
134+
@Override
135+
public Object getVersion(T entity) {
136+
137+
if (!isVersioned()) {
138+
return null;
139+
}
140+
141+
PersistentPropertyAccessor<T> accessor = this.entityMetadata.getPropertyAccessor(entity);
142+
143+
return accessor.getProperty(this.entityMetadata.getRequiredVersionProperty());
144+
}
145+
118146
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Optional;
2424
import java.util.stream.Collectors;
2525

26+
import org.springframework.dao.OptimisticLockingFailureException;
2627
import org.springframework.data.domain.Example;
2728
import org.springframework.data.domain.Page;
2829
import org.springframework.data.domain.PageImpl;
@@ -40,6 +41,8 @@
4041
import org.springframework.lang.Nullable;
4142
import org.springframework.util.Assert;
4243

44+
import com.mongodb.client.result.DeleteResult;
45+
4346
/**
4447
* Repository base implementation for Mongo.
4548
*
@@ -161,7 +164,14 @@ public void delete(T entity) {
161164

162165
Assert.notNull(entity, "The given entity must not be null!");
163166

164-
mongoOperations.remove(entity, entityInformation.getCollectionName());
167+
DeleteResult deleteResult = mongoOperations.remove(entity, entityInformation.getCollectionName());
168+
169+
if (entityInformation.isVersioned() && deleteResult.wasAcknowledged() && deleteResult.getDeletedCount() == 0) {
170+
throw new OptimisticLockingFailureException(String.format(
171+
"The entity with id %s with version %s in %s cannot be deleted! Was it modified or deleted in the meantime?",
172+
entityInformation.getId(entity), entityInformation.getVersion(entity),
173+
entityInformation.getCollectionName()));
174+
}
165175
}
166176

167177
/*

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
import org.reactivestreams.Publisher;
3030
import org.springframework.dao.IncorrectResultSizeDataAccessException;
31+
import org.springframework.dao.OptimisticLockingFailureException;
3132
import org.springframework.data.domain.Example;
3233
import org.springframework.data.domain.Sort;
3334
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
@@ -39,6 +40,8 @@
3940
import org.springframework.data.util.Streamable;
4041
import org.springframework.util.Assert;
4142

43+
import com.mongodb.client.result.DeleteResult;
44+
4245
/**
4346
* Reactive repository base implementation for Mongo.
4447
*
@@ -355,7 +358,24 @@ public Mono<Void> delete(T entity) {
355358

356359
Assert.notNull(entity, "The given entity must not be null!");
357360

358-
return mongoOperations.remove(entity, entityInformation.getCollectionName()).then();
361+
Mono<DeleteResult> remove = mongoOperations.remove(entity, entityInformation.getCollectionName());
362+
363+
if (entityInformation.isVersioned()) {
364+
365+
remove = remove.handle((deleteResult, sink) -> {
366+
367+
if (deleteResult.wasAcknowledged() && deleteResult.getDeletedCount() == 0) {
368+
sink.error(new OptimisticLockingFailureException(String.format(
369+
"The entity with id %s with version %s in %s cannot be deleted! Was it modified or deleted in the meantime?",
370+
entityInformation.getId(entity), entityInformation.getVersion(entity),
371+
entityInformation.getCollectionName())));
372+
} else {
373+
sink.next(deleteResult);
374+
}
375+
});
376+
}
377+
378+
return remove.then();
359379
}
360380

361381
/*
@@ -367,7 +387,7 @@ public Mono<Void> deleteAll(Iterable<? extends T> entities) {
367387

368388
Assert.notNull(entities, "The given Iterable of entities must not be null!");
369389

370-
return Flux.fromIterable(entities).flatMap(entity -> deleteById(entityInformation.getRequiredId(entity))).then();
390+
return Flux.fromIterable(entities).flatMap(this::delete).then();
371391
}
372392

373393
/*

0 commit comments

Comments
 (0)