Compare commits

...

3 Commits

Author SHA1 Message Date
Christoph Strobl
28191fd505 DATAMONGO-2285 - Polishing.
Preserve existing logic translating com.mongodb.MongoBulkWriteException that might be thrown by calling MongoOperations.insertAll(Collection), and move bulk operation translation to DefaultBulkOperations.
Along the lines remove the no longer used PersistenceExceptionTranslator from DefaultBulkOperations.
2020-06-23 08:49:48 +02:00
Jacob Botuck
ddbc49643f DATAMONGO-2285 - Throw BulkOperationException instead of translated one when running bulk operations.
Return BulkOperationException unless it is a writeConcernError in which case use existing behavior.
2020-06-23 08:43:08 +02:00
Christoph Strobl
c91d18c113 DATAMONGO-2285 - Prepare issue branch. 2020-06-23 08:39:27 +02:00
8 changed files with 63 additions and 26 deletions

View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.1.0-DATAMONGO-2285-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Data MongoDB</name>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.1.0-DATAMONGO-2285-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.1.0-DATAMONGO-2285-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.1.0-DATAMONGO-2285-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -24,8 +24,9 @@ import java.util.stream.Collectors;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.mongodb.BulkOperationException;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.convert.UpdateMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
@@ -46,6 +47,7 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.mongodb.MongoBulkWriteException;
import com.mongodb.WriteConcern;
import com.mongodb.bulk.BulkWriteResult;
import com.mongodb.client.MongoCollection;
@@ -62,6 +64,7 @@ import com.mongodb.client.model.*;
* @author Jens Schauder
* @author Michail Nikolaev
* @author Roman Puchkovskiy
* @author Jacob Botuck
* @since 1.9
*/
class DefaultBulkOperations implements BulkOperations {
@@ -71,7 +74,6 @@ class DefaultBulkOperations implements BulkOperations {
private final BulkOperationContext bulkOperationContext;
private final List<SourceAwareWriteModelHolder> models = new ArrayList<>();
private PersistenceExceptionTranslator exceptionTranslator;
private @Nullable WriteConcern defaultWriteConcern;
private BulkWriteOptions bulkOptions;
@@ -95,19 +97,9 @@ class DefaultBulkOperations implements BulkOperations {
this.mongoOperations = mongoOperations;
this.collectionName = collectionName;
this.bulkOperationContext = bulkOperationContext;
this.exceptionTranslator = new MongoExceptionTranslator();
this.bulkOptions = getBulkWriteOptions(bulkOperationContext.getBulkMode());
}
/**
* Configures the {@link PersistenceExceptionTranslator} to be used. Defaults to {@link MongoExceptionTranslator}.
*
* @param exceptionTranslator can be {@literal null}.
*/
public void setExceptionTranslator(@Nullable PersistenceExceptionTranslator exceptionTranslator) {
this.exceptionTranslator = exceptionTranslator == null ? new MongoExceptionTranslator() : exceptionTranslator;
}
/**
* Configures the default {@link WriteConcern} to be used. Defaults to {@literal null}.
*
@@ -314,11 +306,25 @@ class DefaultBulkOperations implements BulkOperations {
collection = collection.withWriteConcern(defaultWriteConcern);
}
return collection.bulkWrite( //
models.stream() //
.map(this::extractAndMapWriteModel) //
.collect(Collectors.toList()), //
bulkOptions);
try {
return collection.bulkWrite( //
models.stream() //
.map(this::extractAndMapWriteModel) //
.collect(Collectors.toList()), //
bulkOptions);
} catch (Exception ex) {
if (ex instanceof MongoBulkWriteException) {
MongoBulkWriteException mongoBulkWriteException = (MongoBulkWriteException) ex;
if (mongoBulkWriteException.getWriteConcernError() != null) {
throw new DataIntegrityViolationException(ex.getMessage(), ex);
}
throw new BulkOperationException(ex.getMessage(), mongoBulkWriteException);
}
throw ex;
}
}
private WriteModel<Document> extractAndMapWriteModel(SourceAwareWriteModelHolder it) {

View File

@@ -757,7 +757,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
new BulkOperationContext(mode, Optional.ofNullable(getPersistentEntity(entityType)), queryMapper, updateMapper,
eventPublisher, entityCallbacks));
operations.setExceptionTranslator(exceptionTranslator);
operations.setDefaultWriteConcern(writeConcern);
return operations;

View File

@@ -27,6 +27,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.mongodb.BulkOperationException;
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
import org.springframework.data.mongodb.core.DefaultBulkOperations.BulkOperationContext;
import org.springframework.data.mongodb.core.convert.QueryMapper;
@@ -91,13 +92,13 @@ public class DefaultBulkOperationsIntegrationTests {
assertThat(createBulkOps(BulkMode.ORDERED).insert(documents).execute().getInsertedCount()).isEqualTo(2);
}
@Test // DATAMONGO-934
@Test // DATAMONGO-934, DATAMONGO-2285
public void insertOrderedFails() {
List<BaseDoc> documents = Arrays.asList(newDoc("1"), newDoc("1"), newDoc("2"));
assertThatThrownBy(() -> createBulkOps(BulkMode.ORDERED).insert(documents).execute()) //
.isInstanceOf(DuplicateKeyException.class) //
.isInstanceOf(BulkOperationException.class) //
.hasCauseInstanceOf(MongoBulkWriteException.class) //
.extracting(Throwable::getCause) //
.satisfies(it -> {
@@ -117,13 +118,13 @@ public class DefaultBulkOperationsIntegrationTests {
assertThat(createBulkOps(BulkMode.UNORDERED).insert(documents).execute().getInsertedCount()).isEqualTo(2);
}
@Test // DATAMONGO-934
@Test // DATAMONGO-934, DATAMONGO-2285
public void insertUnOrderedContinuesOnError() {
List<BaseDoc> documents = Arrays.asList(newDoc("1"), newDoc("1"), newDoc("2"));
assertThatThrownBy(() -> createBulkOps(BulkMode.UNORDERED).insert(documents).execute()) //
.isInstanceOf(DuplicateKeyException.class) //
.isInstanceOf(BulkOperationException.class) //
.hasCauseInstanceOf(MongoBulkWriteException.class) //
.extracting(Throwable::getCause) //
.satisfies(it -> {

View File

@@ -24,6 +24,7 @@ import static org.mockito.Mockito.eq;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@@ -40,9 +41,11 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.annotation.Id;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.mongodb.BulkOperationException;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
import org.springframework.data.mongodb.core.DefaultBulkOperations.BulkOperationContext;
@@ -64,9 +67,13 @@ import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Update;
import com.mongodb.MongoBulkWriteException;
import com.mongodb.MongoWriteException;
import com.mongodb.ServerAddress;
import com.mongodb.WriteConcern;
import com.mongodb.WriteError;
import com.mongodb.bulk.BulkWriteError;
import com.mongodb.bulk.WriteConcernError;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.BulkWriteOptions;
@@ -85,6 +92,7 @@ import com.mongodb.client.model.WriteModel;
* @author Minsu Kim
* @author Jens Schauder
* @author Roman Puchkovskiy
* @author Jacob Botuck
*/
@ExtendWith(MockitoExtension.class)
class DefaultBulkOperationsUnitTests {
@@ -367,6 +375,29 @@ class DefaultBulkOperationsUnitTests {
.isEqualTo(new Document("$set", new Document("items.$.documents.0.the_file_id", "file-id")));
}
@Test // DATAMONGO-2285
public void translateMongoBulkOperationExceptionWithWriteConcernError() {
when(collection.bulkWrite(anyList(), any(BulkWriteOptions.class))).thenThrow(new MongoBulkWriteException(null,
Collections.emptyList(),
new WriteConcernError(42, "codename", "writeconcern error happened", new BsonDocument()), new ServerAddress()));
assertThatExceptionOfType(DataIntegrityViolationException.class)
.isThrownBy(() -> ops.insert(new SomeDomainType()).execute());
}
@Test // DATAMONGO-2285
public void translateMongoBulkOperationExceptionWithoutWriteConcernError() {
when(collection.bulkWrite(anyList(), any(BulkWriteOptions.class))).thenThrow(new MongoBulkWriteException(null,
Collections.singletonList(new BulkWriteError(42, "a write error happened", new BsonDocument(), 49)), null,
new ServerAddress()));
assertThatExceptionOfType(BulkOperationException.class)
.isThrownBy(() -> ops.insert(new SomeDomainType()).execute());
}
static class OrderTest {
String id;