Skip to content

Commit 777d478

Browse files
DATAMONGO-2195 - Move common logic to ResultOperations.
And guard transaction tests.
1 parent d68636f commit 777d478

File tree

5 files changed

+122
-51
lines changed

5 files changed

+122
-51
lines changed

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,7 @@ interface Entity<T> {
206206
* @since 2.2
207207
*/
208208
default Query getRemoveByQuery() {
209-
210-
if (isVersionedEntity()) {
211-
return getQueryForVersion();
212-
}
213-
214-
return getByIdQuery();
209+
return isVersionedEntity() ? getQueryForVersion() : getByIdQuery();
215210
}
216211

217212
/**

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

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1740,29 +1740,25 @@ public DeleteResult doInCollection(MongoCollection<Document> collection)
17401740
private void checkForOptimisticLockingFailures(DeleteResult result, Document removeQuery,
17411741
MongoCollection<Document> collectionToUse) {
17421742

1743-
if (!multi && entity != null && entity.hasVersionProperty() && result.wasAcknowledged()) {
1744-
1745-
if (result.getDeletedCount() == 0) {
1746-
1747-
String versionFieldName = entity.getVersionProperty().getFieldName();
1748-
Document idQuery = new Document(removeQuery);
1749-
idQuery.remove(versionFieldName);
1743+
if (multi || !ResultOperations.isUndecidedDeleteResult(result, removeQuery, entity)) {
1744+
return;
1745+
}
17501746

1751-
Iterator<Document> it = collectionToUse.find(idQuery)
1752-
.projection(Projections.include("_id", versionFieldName)).limit(1).iterator();
1747+
String versionFieldName = entity.getVersionProperty().getFieldName();
1748+
Document idQuery = new Document(removeQuery);
1749+
idQuery.remove(versionFieldName);
17531750

1754-
if (it.hasNext()) {
1751+
Iterator<Document> it = collectionToUse.find(idQuery).projection(Projections.include("_id", versionFieldName))
1752+
.limit(1).iterator();
17551753

1756-
Document source = it.next();
1754+
if (it.hasNext()) {
17571755

1758-
throw new OptimisticLockingFailureException(String
1759-
.format("The entity with id %s in %s has changed and cannot be deleted! " + System.lineSeparator() + //
1760-
"Expected version %s but was %s.", source.get("_id"), collectionName, removeQuery.get(versionFieldName),
1761-
source.get(versionFieldName)));
1762-
}
1756+
Document source = it.next();
1757+
throw ResultOperations.newDeleteVersionedOptimisticLockingException(source.get("_id"), collectionName,
1758+
removeQuery.get(versionFieldName), source.get(versionFieldName));
17631759

1764-
}
17651760
}
1761+
17661762
}
17671763
});
17681764
}

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

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,7 +1688,7 @@ private boolean containsVersionProperty(Document document, @Nullable MongoPersis
16881688
return false;
16891689
}
16901690

1691-
return document.containsKey(persistentEntity.getRequiredIdProperty().getFieldName());
1691+
return document.containsKey(persistentEntity.getRequiredVersionProperty().getFieldName());
16921692
}
16931693

16941694
/*
@@ -1826,41 +1826,33 @@ protected <T> Mono<DeleteResult> doRemove(String collectionName, Query query, @N
18261826

18271827
}).flatMap(deleteResult -> {
18281828

1829-
if (entity != null && entity.hasVersionProperty()) {
1830-
1831-
if (deleteResult.wasAcknowledged() && deleteResult.getDeletedCount() == 0) {
1832-
1833-
if (query.getQueryObject().containsKey(entity.getVersionProperty().getFieldName())) {
1829+
if (!ResultOperations.isUndecidedDeleteResult(deleteResult, removeQuery, entity)) {
1830+
return Mono.just(deleteResult);
1831+
}
18341832

1835-
return execute(collectionName, (coll -> {
1833+
return execute(collectionName, (coll -> {
18361834

1837-
String versionFieldName = entity.getVersionProperty().getFieldName();
1835+
String versionFieldName = entity.getVersionProperty().getFieldName();
18381836

1839-
Document daQuery = new Document(removeQuery);
1840-
daQuery.remove(versionFieldName);
1837+
Document queryWithoutVersion = new Document(removeQuery);
1838+
queryWithoutVersion.remove(versionFieldName);
18411839

1842-
Publisher<Document> publisher = coll.find(daQuery)
1843-
.projection(Projections.include("_id", entity.getVersionProperty().getFieldName())).limit(1).first();
1840+
Publisher<Document> publisher = coll.find(queryWithoutVersion)
1841+
.projection(Projections.include("_id", entity.getVersionProperty().getFieldName())).limit(1).first();
18441842

1845-
return Mono.from(publisher) //
1846-
.defaultIfEmpty(new Document()) //
1847-
.flatMap(it -> {
1843+
return Mono.from(publisher) //
1844+
.defaultIfEmpty(new Document()) //
1845+
.flatMap(it -> {
18481846

1849-
if (it.isEmpty()) {
1850-
return Mono.just(deleteResult);
1851-
}
1852-
return Mono.error(new OptimisticLockingFailureException(String.format(
1853-
"The entity with id %s in %s has changed and cannot be deleted! " + System.lineSeparator() + //
1854-
"Expected version %s but was %s.", it.get("_id"), collectionName, removeQuery.get(versionFieldName),
1855-
it.get(versionFieldName))));
1847+
if (it.isEmpty()) {
1848+
return Mono.just(deleteResult);
1849+
}
18561850

1857-
});
1858-
})).next();
1859-
}
1860-
}
1861-
}
1851+
return Mono.error(ResultOperations.newDeleteVersionedOptimisticLockingException(it.get("_id"),
1852+
collectionName, removeQuery.get(versionFieldName), it.get(versionFieldName)));
1853+
});
1854+
})).next();
18621855

1863-
return Mono.just(deleteResult);
18641856
}).doOnNext(it -> maybeEmitEvent(new AfterDeleteEvent<>(queryObject, entityClass, collectionName))) //
18651857
.next();
18661858
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.core;
17+
18+
import org.bson.Document;
19+
import org.springframework.dao.OptimisticLockingFailureException;
20+
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
21+
import org.springframework.lang.Nullable;
22+
23+
import com.mongodb.client.result.DeleteResult;
24+
import com.mongodb.client.result.UpdateResult;
25+
26+
/**
27+
* {@link ResultOperations} is intended to share logic depending on {@link DeleteResult} and
28+
* {@link com.mongodb.client.result.UpdateResult} between reactive and imperative implementations.
29+
*
30+
* @author Christoph Strobl
31+
* @since 2.2
32+
*/
33+
class ResultOperations {
34+
35+
/**
36+
* Decide if a given {@link DeleteResult} needs further inspection. Returns {@literal true} if the delete of a
37+
* {@link org.springframework.data.annotation.Version versioned} entity did not remove any documents although the
38+
* operation has been {@link DeleteResult#wasAcknowledged() acknowledged}.
39+
*
40+
* @param deleteResult must not be {@literal null}.
41+
* @param query the actual query used for the delete operation.
42+
* @param entity can be {@literal null}.
43+
* @return {@literal true} if it cannot be decided if nothing got deleted because of a potential
44+
* {@link org.springframework.data.annotation.Version version} mismatch, or if the document did not exist in
45+
* first place.
46+
*/
47+
static boolean isUndecidedDeleteResult(DeleteResult deleteResult, org.bson.Document query,
48+
@Nullable MongoPersistentEntity<?> entity) {
49+
50+
if (!deleteResult.wasAcknowledged() || deleteResult.getDeletedCount() > 0) {
51+
return false;
52+
}
53+
54+
if (entity == null) {
55+
return false;
56+
}
57+
58+
if (!entity.hasVersionProperty()) {
59+
return false;
60+
}
61+
62+
return containsVersionProperty(query, entity);
63+
}
64+
65+
static OptimisticLockingFailureException newDeleteVersionedOptimisticLockingException(Object id,
66+
String collectionName, Object expectedVersion, Object actualVersion) {
67+
68+
throw new OptimisticLockingFailureException(
69+
String.format("The entity with id %s in %s has changed and cannot be deleted! " + System.lineSeparator() + //
70+
"Expected version %s but was %s.", id, collectionName, expectedVersion, actualVersion));
71+
}
72+
73+
private static boolean containsVersionProperty(Document document, MongoPersistentEntity<?> entity) {
74+
return document.containsKey(entity.getRequiredVersionProperty().getFieldName());
75+
}
76+
}

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryVersionedEntityTests.java

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

1818
import static org.assertj.core.api.Assertions.*;
19+
import static org.assertj.core.api.Assumptions.assumeThat;
1920
import static org.springframework.data.mongodb.core.query.Criteria.*;
2021
import static org.springframework.data.mongodb.core.query.Query.*;
2122

@@ -32,6 +33,8 @@
3233
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
3334
import org.springframework.data.mongodb.repository.VersionedPerson;
3435
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
36+
import org.springframework.data.mongodb.test.util.MongoVersion;
37+
import org.springframework.data.mongodb.test.util.ReplicaSet;
3538
import org.springframework.data.util.ClassTypeInformation;
3639
import org.springframework.test.context.ContextConfiguration;
3740
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -91,8 +94,11 @@ public void deleteWithMatchingVersion() {
9194
}
9295

9396
@Test // DATAMONGO-2195
97+
@MongoVersion(asOf = "4.0")
9498
public void deleteWithMatchingVersionInTx() {
9599

100+
assumeThat(ReplicaSet.required().runsAsReplicaSet()).isTrue();
101+
96102
long countBefore = repository.count();
97103

98104
initTxTemplate().execute(status -> {
@@ -118,8 +124,11 @@ public void deleteWithVersionMismatch() {
118124
}
119125

120126
@Test // DATAMONGO-2195
127+
@MongoVersion(asOf = "4.0")
121128
public void deleteWithVersionMismatchInTx() {
122129

130+
assumeThat(ReplicaSet.required().runsAsReplicaSet()).isTrue();
131+
123132
long countBefore = repository.count();
124133

125134
assertThatExceptionOfType(OptimisticLockingFailureException.class)
@@ -141,8 +150,11 @@ public void deleteNonExisting() {
141150
}
142151

143152
@Test // DATAMONGO-2195
153+
@MongoVersion(asOf = "4.0")
144154
public void deleteNonExistingInTx() {
145155

156+
assumeThat(ReplicaSet.required().runsAsReplicaSet()).isTrue();
157+
146158
initTxTemplate().execute(status -> {
147159

148160
repository.delete(new VersionedPerson("T-800"));

0 commit comments

Comments
 (0)