diff --git a/lombok.config b/lombok.config new file mode 100644 index 000000000..e50c7ea43 --- /dev/null +++ b/lombok.config @@ -0,0 +1,2 @@ +lombok.nonNull.exceptionType = IllegalArgumentException +lombok.log.fieldName = LOG diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/BulkOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/BulkOperations.java index c5be41fb3..297fbd0ea 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/BulkOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/BulkOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2016 the original author or authors. + * Copyright 2015-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ import com.mongodb.bulk.BulkWriteResult; * 2.6 and make use of low level bulk commands on the protocol level. This interface defines a fluent API to add * multiple single operations or list of similar operations in sequence which can then eventually be executed by calling * {@link #execute()}. - * + * * @author Tobias Trelle * @author Oliver Gierke * @since 1.9 @@ -49,7 +49,7 @@ public interface BulkOperations { /** * Add a single insert to the bulk operation. - * + * * @param documents the document to insert, must not be {@literal null}. * @return the current {@link BulkOperations} instance with the insert added, will never be {@literal null}. */ @@ -57,7 +57,7 @@ public interface BulkOperations { /** * Add a list of inserts to the bulk operation. - * + * * @param documents List of documents to insert, must not be {@literal null}. * @return the current {@link BulkOperations} instance with the insert added, will never be {@literal null}. */ @@ -65,7 +65,7 @@ public interface BulkOperations { /** * Add a single update to the bulk operation. For the update request, only the first matching document is updated. - * + * * @param query update criteria, must not be {@literal null}. * @param update {@link Update} operation to perform, must not be {@literal null}. * @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}. @@ -74,7 +74,7 @@ public interface BulkOperations { /** * Add a list of updates to the bulk operation. For each update request, only the first matching document is updated. - * + * * @param updates Update operations to perform. * @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}. */ @@ -82,7 +82,7 @@ public interface BulkOperations { /** * Add a single update to the bulk operation. For the update request, all matching documents are updated. - * + * * @param query Update criteria. * @param update Update operation to perform. * @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}. @@ -91,7 +91,7 @@ public interface BulkOperations { /** * Add a list of updates to the bulk operation. For each update request, all matching documents are updated. - * + * * @param updates Update operations to perform. * @return The bulk operation. * @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}. @@ -101,7 +101,7 @@ public interface BulkOperations { /** * Add a single upsert to the bulk operation. An upsert is an update if the set of matching documents is not empty, * else an insert. - * + * * @param query Update criteria. * @param update Update operation to perform. * @return The bulk operation. @@ -112,7 +112,7 @@ public interface BulkOperations { /** * Add a list of upserts to the bulk operation. An upsert is an update if the set of matching documents is not empty, * else an insert. - * + * * @param updates Updates/insert operations to perform. * @return The bulk operation. * @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}. @@ -121,7 +121,7 @@ public interface BulkOperations { /** * Add a single remove operation to the bulk operation. - * + * * @param remove the {@link Query} to select the documents to be removed, must not be {@literal null}. * @return the current {@link BulkOperations} instance with the removal added, will never be {@literal null}. */ @@ -129,7 +129,7 @@ public interface BulkOperations { /** * Add a list of remove operations to the bulk operation. - * + * * @param removes the remove operations to perform, must not be {@literal null}. * @return the current {@link BulkOperations} instance with the removal added, will never be {@literal null}. */ @@ -137,9 +137,9 @@ public interface BulkOperations { /** * Execute all bulk operations using the default write concern. - * + * * @return Result of the bulk operation providing counters for inserts/updates etc. - * @throws {@link BulkOperationException} if an error occurred during bulk processing. + * @throws org.springframework.data.mongodb.BulkOperationException if an error occurred during bulk processing. */ BulkWriteResult execute(); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultBulkOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultBulkOperations.java index 6c63ad49b..5ab965edb 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultBulkOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultBulkOperations.java @@ -15,10 +15,11 @@ */ package org.springframework.data.mongodb.core; -import lombok.Data; +import lombok.NonNull; +import lombok.Value; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -54,6 +55,7 @@ import com.mongodb.client.model.WriteModel; * @author Tobias Trelle * @author Oliver Gierke * @author Christoph Strobl + * @author Mark Paluch * @since 1.9 */ class DefaultBulkOperations implements BulkOperations { @@ -61,15 +63,13 @@ class DefaultBulkOperations implements BulkOperations { private final MongoOperations mongoOperations; private final String collectionName; private final BulkOperationContext bulkOperationContext; + private final List> models = new ArrayList<>(); - private WriteConcernResolver writeConcernResolver; private PersistenceExceptionTranslator exceptionTranslator; private WriteConcern defaultWriteConcern; private BulkWriteOptions bulkOptions; - List> models = new ArrayList<>(); - /** * Creates a new {@link DefaultBulkOperations} for the given {@link MongoOperations}, collection name and * {@link BulkOperationContext}. @@ -90,7 +90,7 @@ class DefaultBulkOperations implements BulkOperations { this.collectionName = collectionName; this.bulkOperationContext = bulkOperationContext; this.exceptionTranslator = new MongoExceptionTranslator(); - this.bulkOptions = initBulkOperation(); + this.bulkOptions = getBulkWriteOptions(bulkOperationContext.getBulkMode()); } /** @@ -102,22 +102,12 @@ class DefaultBulkOperations implements BulkOperations { this.exceptionTranslator = exceptionTranslator == null ? new MongoExceptionTranslator() : exceptionTranslator; } - /** - * Configures the {@link WriteConcernResolver} to be used. Defaults to {@link DefaultWriteConcernResolver}. - * - * @param writeConcernResolver can be {@literal null}. - */ - public void setWriteConcernResolver(WriteConcernResolver writeConcernResolver) { - this.writeConcernResolver = writeConcernResolver == null ? DefaultWriteConcernResolver.INSTANCE - : writeConcernResolver; - } - /** * Configures the default {@link WriteConcern} to be used. Defaults to {@literal null}. * * @param defaultWriteConcern can be {@literal null}. */ - public void setDefaultWriteConcern(WriteConcern defaultWriteConcern) { + void setDefaultWriteConcern(WriteConcern defaultWriteConcern) { this.defaultWriteConcern = defaultWriteConcern; } @@ -140,6 +130,7 @@ class DefaultBulkOperations implements BulkOperations { mongoOperations.getConverter().write(document, sink); models.add(new InsertOneModel<>(sink)); + return this; } @@ -152,9 +143,7 @@ class DefaultBulkOperations implements BulkOperations { Assert.notNull(documents, "Documents must not be null!"); - for (Object document : documents) { - insert(document); - } + documents.forEach(this::insert); return this; } @@ -170,7 +159,7 @@ class DefaultBulkOperations implements BulkOperations { Assert.notNull(query, "Query must not be null!"); Assert.notNull(update, "Update must not be null!"); - return updateOne(Arrays.asList(Pair.of(query, update))); + return updateOne(Collections.singletonList(Pair.of(query, update))); } /* @@ -200,7 +189,7 @@ class DefaultBulkOperations implements BulkOperations { Assert.notNull(query, "Query must not be null!"); Assert.notNull(update, "Update must not be null!"); - return updateMulti(Arrays.asList(Pair.of(query, update))); + return updateMulti(Collections.singletonList(Pair.of(query, update))); } /* @@ -254,7 +243,8 @@ class DefaultBulkOperations implements BulkOperations { DeleteOptions deleteOptions = new DeleteOptions(); query.getCollation().map(Collation::toMongoCollation).ifPresent(deleteOptions::collation); - models.add(new DeleteManyModel(query.getQueryObject(), deleteOptions)); + models.add(new DeleteManyModel<>(query.getQueryObject(), deleteOptions)); + return this; } @@ -296,13 +286,13 @@ class DefaultBulkOperations implements BulkOperations { throw toThrow == null ? o_O : toThrow; } finally { - this.bulkOptions = initBulkOperation(); + this.bulkOptions = getBulkWriteOptions(bulkOperationContext.getBulkMode()); } } /** * Performs update and upsert bulk operations. - * + * * @param query the {@link Query} to determine documents to update. * @param update the {@link Update} to perform, must not be {@literal null}. * @param upsert whether to upsert. @@ -323,20 +313,8 @@ class DefaultBulkOperations implements BulkOperations { } else { models.add(new UpdateOneModel<>(query.getQueryObject(), update.getUpdateObject(), options)); } - return this; - } - - private final BulkWriteOptions initBulkOperation() { - BulkWriteOptions options = new BulkWriteOptions(); - - switch (bulkOperationContext.getBulkMode()) { - case ORDERED: - return options.ordered(true); - case UNORDERED: - return options.ordered(false); - } - throw new IllegalStateException("BulkMode was null!"); + return this; } private WriteModel mapWriteModel(WriteModel writeModel) { @@ -345,7 +323,7 @@ class DefaultBulkOperations implements BulkOperations { UpdateOneModel model = (UpdateOneModel) writeModel; - return new UpdateOneModel(getMappedQuery(model.getFilter()), getMappedUpdate(model.getUpdate()), + return new UpdateOneModel<>(getMappedQuery(model.getFilter()), getMappedUpdate(model.getUpdate()), model.getOptions()); } @@ -353,7 +331,7 @@ class DefaultBulkOperations implements BulkOperations { UpdateManyModel model = (UpdateManyModel) writeModel; - return new UpdateManyModel(getMappedQuery(model.getFilter()), getMappedUpdate(model.getUpdate()), + return new UpdateManyModel<>(getMappedQuery(model.getFilter()), getMappedUpdate(model.getUpdate()), model.getOptions()); } @@ -361,14 +339,14 @@ class DefaultBulkOperations implements BulkOperations { DeleteOneModel model = (DeleteOneModel) writeModel; - return new DeleteOneModel(getMappedQuery(model.getFilter()), model.getOptions()); + return new DeleteOneModel<>(getMappedQuery(model.getFilter()), model.getOptions()); } if (writeModel instanceof DeleteManyModel) { DeleteManyModel model = (DeleteManyModel) writeModel; - return new DeleteManyModel(getMappedQuery(model.getFilter()), model.getOptions()); + return new DeleteManyModel<>(getMappedQuery(model.getFilter()), model.getOptions()); } return writeModel; @@ -382,6 +360,20 @@ class DefaultBulkOperations implements BulkOperations { return bulkOperationContext.getQueryMapper().getMappedObject(query, bulkOperationContext.getEntity()); } + private static BulkWriteOptions getBulkWriteOptions(BulkMode bulkMode) { + + BulkWriteOptions options = new BulkWriteOptions(); + + switch (bulkMode) { + case ORDERED: + return options.ordered(true); + case UNORDERED: + return options.ordered(false); + } + + throw new IllegalStateException("BulkMode was null!"); + } + /** * {@link BulkOperationContext} holds information about * {@link org.springframework.data.mongodb.core.BulkOperations.BulkMode} the entity in use as well as references to @@ -390,12 +382,12 @@ class DefaultBulkOperations implements BulkOperations { * @author Christoph Strobl * @since 2.0 */ - @Data + @Value static class BulkOperationContext { - final BulkMode bulkMode; - final Optional> entity; - final QueryMapper queryMapper; - final UpdateMapper updateMapper; + @NonNull BulkMode bulkMode; + @NonNull Optional> entity; + @NonNull QueryMapper queryMapper; + @NonNull UpdateMapper updateMapper; } } 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 88612c16c..d22578da0 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 @@ -562,7 +562,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, new BulkOperationContext(mode, getPersistentEntity(entityType), queryMapper, updateMapper)); operations.setExceptionTranslator(exceptionTranslator); - operations.setWriteConcernResolver(writeConcernResolver); operations.setDefaultWriteConcern(writeConcern); return operations; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultBulkOperationsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultBulkOperationsUnitTests.java index d81b5c139..dccd74491 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultBulkOperationsUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultBulkOperationsUnitTests.java @@ -29,6 +29,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.data.annotation.Id; @@ -51,14 +52,18 @@ import com.mongodb.client.model.UpdateOneModel; import com.mongodb.client.model.WriteModel; /** + * Unit tests for {@link DefaultBulkOperations}. + * * @author Christoph Strobl + * @author Mark Paluch */ @RunWith(MockitoJUnitRunner.class) public class DefaultBulkOperationsUnitTests { @Mock MongoTemplate template; - @Mock MongoCollection collection; + @Mock MongoCollection collection; @Mock DbRefResolver dbRefResolver; + @Captor ArgumentCaptor>> captor; MongoConverter converter; MongoMappingContext mappingContext; @@ -85,27 +90,23 @@ public class DefaultBulkOperationsUnitTests { ops.updateOne(new BasicQuery("{}").collation(Collation.of("de")), new Update().set("lastName", "targaryen")) .execute(); - ArgumentCaptor>> captor = ArgumentCaptor.forClass(List.class); - verify(collection).bulkWrite(captor.capture(), any()); assertThat(captor.getValue().get(0)).isInstanceOf(UpdateOneModel.class); - assertThat(((UpdateOneModel) captor.getValue().get(0)).getOptions().getCollation()) + assertThat(((UpdateOneModel) captor.getValue().get(0)).getOptions().getCollation()) .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de").build()); } @Test // DATAMONGO-1518 - public void updateMayShouldUseCollationWhenPresent() { + public void updateManyShouldUseCollationWhenPresent() { ops.updateMulti(new BasicQuery("{}").collation(Collation.of("de")), new Update().set("lastName", "targaryen")) .execute(); - ArgumentCaptor>> captor = ArgumentCaptor.forClass(List.class); - verify(collection).bulkWrite(captor.capture(), any()); assertThat(captor.getValue().get(0)).isInstanceOf(UpdateManyModel.class); - assertThat(((UpdateManyModel) captor.getValue().get(0)).getOptions().getCollation()) + assertThat(((UpdateManyModel) captor.getValue().get(0)).getOptions().getCollation()) .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de").build()); } @@ -114,12 +115,10 @@ public class DefaultBulkOperationsUnitTests { ops.remove(new BasicQuery("{}").collation(Collation.of("de"))).execute(); - ArgumentCaptor>> captor = ArgumentCaptor.forClass(List.class); - verify(collection).bulkWrite(captor.capture(), any()); assertThat(captor.getValue().get(0)).isInstanceOf(DeleteManyModel.class); - assertThat(((DeleteManyModel) captor.getValue().get(0)).getOptions().getCollation()) + assertThat(((DeleteManyModel) captor.getValue().get(0)).getOptions().getCollation()) .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de").build()); } @@ -128,11 +127,9 @@ public class DefaultBulkOperationsUnitTests { ops.updateOne(query(where("firstName").is("danerys")), Update.update("firstName", "queen danerys")).execute(); - ArgumentCaptor>> captor = ArgumentCaptor.forClass(List.class); - verify(collection).bulkWrite(captor.capture(), any()); - UpdateOneModel updateModel = (UpdateOneModel) captor.getValue().get(0); + UpdateOneModel updateModel = (UpdateOneModel) captor.getValue().get(0); assertThat(updateModel.getFilter()).isEqualTo(new Document("first_name", "danerys")); assertThat(updateModel.getUpdate()).isEqualTo(new Document("$set", new Document("first_name", "queen danerys"))); } @@ -142,11 +139,9 @@ public class DefaultBulkOperationsUnitTests { ops.remove(query(where("firstName").is("danerys"))).execute(); - ArgumentCaptor>> captor = ArgumentCaptor.forClass(List.class); - verify(collection).bulkWrite(captor.capture(), any()); - DeleteManyModel updateModel = (DeleteManyModel) captor.getValue().get(0); + DeleteManyModel updateModel = (DeleteManyModel) captor.getValue().get(0); assertThat(updateModel.getFilter()).isEqualTo(new Document("first_name", "danerys")); }