Browse Source

DATAMONGO-1678 - Run bulk update / remove documents through type mappers.

We now make sure to run any query / update object through the Query- / UpdateMapper. This ensures @Field annotations and potential custom conversions get processed correctly for update / remove operations.

Original pull request: #472.
pull/482/head
Christoph Strobl 9 years ago committed by Mark Paluch
parent
commit
1f7b0ac40b
  1. 83
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultBulkOperations.java
  2. 4
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
  3. 36
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultBulkOperationsIntegrationTests.java

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

@ -15,11 +15,16 @@ @@ -15,11 +15,16 @@
*/
package org.springframework.data.mongodb.core;
import lombok.Data;
import java.util.Arrays;
import java.util.List;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.convert.UpdateMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.util.Pair;
@ -45,40 +50,35 @@ import com.mongodb.WriteConcern; @@ -45,40 +50,35 @@ import com.mongodb.WriteConcern;
class DefaultBulkOperations implements BulkOperations {
private final MongoOperations mongoOperations;
private final BulkMode bulkMode;
private final String collectionName;
private final Class<?> entityType;
private final BulkOperationContext bulkOperationContext;
private PersistenceExceptionTranslator exceptionTranslator;
private WriteConcernResolver writeConcernResolver;
private PersistenceExceptionTranslator exceptionTranslator;
private WriteConcern defaultWriteConcern;
private BulkWriteOperation bulk;
/**
* Creates a new {@link DefaultBulkOperations} for the given {@link MongoOperations}, {@link BulkMode}, collection
* name and {@link WriteConcern}.
* Creates a new {@link DefaultBulkOperations} for the given {@link MongoOperations}, collection name and
* {@link BulkOperationContext}.
*
* @param mongoOperations The underlying {@link MongoOperations}, must not be {@literal null}.
* @param bulkMode must not be {@literal null}.
* @param collectionName Name of the collection to work on, must not be {@literal null} or empty.
* @param entityType the entity type, can be {@literal null}.
* @param mongoOperations must not be {@literal null}.
* @param collectionName must not be {@literal null}.
* @param bulkOperationContext must not be {@literal null}.
* @since 1.9.12
*/
DefaultBulkOperations(MongoOperations mongoOperations, BulkMode bulkMode, String collectionName,
Class<?> entityType) {
DefaultBulkOperations(MongoOperations mongoOperations, String collectionName,
BulkOperationContext bulkOperationContext) {
Assert.notNull(mongoOperations, "MongoOperations must not be null!");
Assert.notNull(bulkMode, "BulkMode must not be null!");
Assert.hasText(collectionName, "Collection name must not be null or empty!");
Assert.hasText(collectionName, "CollectionName must not be null nor empty!");
Assert.notNull(bulkOperationContext, "BulkOperationContext must not be null!");
this.mongoOperations = mongoOperations;
this.bulkMode = bulkMode;
this.collectionName = collectionName;
this.entityType = entityType;
this.bulkOperationContext = bulkOperationContext;
this.exceptionTranslator = new MongoExceptionTranslator();
this.writeConcernResolver = DefaultWriteConcernResolver.INSTANCE;
this.bulk = initBulkOperation();
}
@ -239,7 +239,7 @@ class DefaultBulkOperations implements BulkOperations { @@ -239,7 +239,7 @@ class DefaultBulkOperations implements BulkOperations {
Assert.notNull(query, "Query must not be null!");
bulk.find(query.getQueryObject()).remove();
bulk.find(getMappedQuery(query.getQueryObject())).remove();
return this;
}
@ -267,14 +267,12 @@ class DefaultBulkOperations implements BulkOperations { @@ -267,14 +267,12 @@ class DefaultBulkOperations implements BulkOperations {
@Override
public BulkWriteResult execute() {
MongoAction action = new MongoAction(defaultWriteConcern, MongoActionOperation.BULK, collectionName, entityType,
null, null);
MongoAction action = new MongoAction(defaultWriteConcern, MongoActionOperation.BULK, collectionName,
bulkOperationContext.getEntityType(), null, null);
WriteConcern writeConcern = writeConcernResolver.resolve(action);
try {
return writeConcern == null ? bulk.execute() : bulk.execute(writeConcern);
} catch (BulkWriteException o_O) {
DataAccessException toThrow = exceptionTranslator.translateExceptionIfPossible(o_O);
@ -304,17 +302,17 @@ class DefaultBulkOperations implements BulkOperations { @@ -304,17 +302,17 @@ class DefaultBulkOperations implements BulkOperations {
if (upsert) {
if (multi) {
builder.upsert().update(update.getUpdateObject());
builder.upsert().update(getMappedUpdate(update.getUpdateObject()));
} else {
builder.upsert().updateOne(update.getUpdateObject());
builder.upsert().updateOne(getMappedUpdate(update.getUpdateObject()));
}
} else {
if (multi) {
builder.update(update.getUpdateObject());
builder.update(getMappedUpdate(update.getUpdateObject()));
} else {
builder.updateOne(update.getUpdateObject());
builder.updateOne(getMappedUpdate(update.getUpdateObject()));
}
}
@ -325,7 +323,7 @@ class DefaultBulkOperations implements BulkOperations { @@ -325,7 +323,7 @@ class DefaultBulkOperations implements BulkOperations {
DBCollection collection = mongoOperations.getCollection(collectionName);
switch (bulkMode) {
switch (bulkOperationContext.getBulkMode()) {
case ORDERED:
return collection.initializeOrderedBulkOperation();
case UNORDERED:
@ -334,4 +332,33 @@ class DefaultBulkOperations implements BulkOperations { @@ -334,4 +332,33 @@ class DefaultBulkOperations implements BulkOperations {
throw new IllegalStateException("BulkMode was null!");
}
private DBObject getMappedUpdate(DBObject update) {
return bulkOperationContext.getUpdateMapper().getMappedObject(update, bulkOperationContext.getEntity());
}
private DBObject getMappedQuery(DBObject query) {
return bulkOperationContext.getQueryMapper().getMappedObject(query, bulkOperationContext.getEntity());
}
/**
* {@link BulkOperationContext} holds information about
* {@link org.springframework.data.mongodb.core.BulkOperations.BulkMode} the entity in use as well as references to
* {@link QueryMapper} and {@link UpdateMapper}.
*
* @author Christoph Strobl
* @since 2.0
*/
@Data
static class BulkOperationContext {
final BulkMode bulkMode;
final MongoPersistentEntity<?> entity;
final QueryMapper queryMapper;
final UpdateMapper updateMapper;
Class<?> getEntityType() {
return entity != null ? entity.getType() : null;
}
}
}

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

@ -61,6 +61,7 @@ import org.springframework.data.mapping.model.ConvertingPropertyAccessor; @@ -61,6 +61,7 @@ import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
import org.springframework.data.mongodb.core.DefaultBulkOperations.BulkOperationContext;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
@ -559,7 +560,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -559,7 +560,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
Assert.notNull(mode, "BulkMode must not be null!");
Assert.hasText(collectionName, "Collection name must not be null or empty!");
DefaultBulkOperations operations = new DefaultBulkOperations(this, mode, collectionName, entityType);
DefaultBulkOperations operations = new DefaultBulkOperations(this, collectionName,
new BulkOperationContext(mode, getPersistentEntity(entityType), queryMapper, updateMapper));
operations.setExceptionTranslator(exceptionTranslator);
operations.setWriteConcernResolver(writeConcernResolver);

36
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultBulkOperationsIntegrationTests.java

@ -28,6 +28,10 @@ import org.junit.runner.RunWith; @@ -28,6 +28,10 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
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;
import org.springframework.data.mongodb.core.convert.UpdateMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
@ -70,7 +74,8 @@ public class DefaultBulkOperationsIntegrationTests { @@ -70,7 +74,8 @@ public class DefaultBulkOperationsIntegrationTests {
*/
@Test(expected = IllegalArgumentException.class)
public void rejectsNullMongoOperations() {
new DefaultBulkOperations(null, null, COLLECTION_NAME, null);
new DefaultBulkOperations(null, COLLECTION_NAME, new BulkOperationContext(BulkMode.ORDERED, null, null, null));
}
/**
@ -78,7 +83,7 @@ public class DefaultBulkOperationsIntegrationTests { @@ -78,7 +83,7 @@ public class DefaultBulkOperationsIntegrationTests {
*/
@Test(expected = IllegalArgumentException.class)
public void rejectsNullCollectionName() {
new DefaultBulkOperations(operations, null, null, null);
new DefaultBulkOperations(operations, null, new BulkOperationContext(BulkMode.ORDERED, null, null, null));
}
/**
@ -86,7 +91,7 @@ public class DefaultBulkOperationsIntegrationTests { @@ -86,7 +91,7 @@ public class DefaultBulkOperationsIntegrationTests {
*/
@Test(expected = IllegalArgumentException.class)
public void rejectsEmptyCollectionName() {
new DefaultBulkOperations(operations, null, "", null);
new DefaultBulkOperations(operations, "", new BulkOperationContext(BulkMode.ORDERED, null, null, null));
}
/**
@ -240,7 +245,7 @@ public class DefaultBulkOperationsIntegrationTests { @@ -240,7 +245,7 @@ public class DefaultBulkOperationsIntegrationTests {
@Test
public void mixedBulkOrdered() {
BulkWriteResult result = createBulkOps(BulkMode.ORDERED).insert(newDoc("1", "v1")).//
BulkWriteResult result = createBulkOps(BulkMode.ORDERED, BaseDoc.class).insert(newDoc("1", "v1")).//
updateOne(where("_id", "1"), set("value", "v2")).//
remove(where("value", "v2")).//
execute();
@ -262,8 +267,8 @@ public class DefaultBulkOperationsIntegrationTests { @@ -262,8 +267,8 @@ public class DefaultBulkOperationsIntegrationTests {
List<Pair<Query, Update>> updates = Arrays.asList(Pair.of(where("value", "v2"), set("value", "v3")));
List<Query> removes = Arrays.asList(where("_id", "1"));
BulkWriteResult result = createBulkOps(BulkMode.ORDERED).insert(inserts).updateMulti(updates).remove(removes)
.execute();
BulkWriteResult result = createBulkOps(BulkMode.ORDERED, BaseDoc.class).insert(inserts).updateMulti(updates)
.remove(removes).execute();
assertThat(result, notNullValue());
assertThat(result.getInsertedCount(), is(3));
@ -282,7 +287,7 @@ public class DefaultBulkOperationsIntegrationTests { @@ -282,7 +287,7 @@ public class DefaultBulkOperationsIntegrationTests {
specialDoc.value = "normal-value";
specialDoc.specialValue = "special-value";
createBulkOps(BulkMode.ORDERED).insert(Arrays.asList(specialDoc)).execute();
createBulkOps(BulkMode.ORDERED, SpecialDoc.class).insert(Arrays.asList(specialDoc)).execute();
BaseDoc doc = operations.findOne(where("_id", specialDoc.id), BaseDoc.class, COLLECTION_NAME);
@ -316,11 +321,22 @@ public class DefaultBulkOperationsIntegrationTests { @@ -316,11 +321,22 @@ public class DefaultBulkOperationsIntegrationTests {
}
private BulkOperations createBulkOps(BulkMode mode) {
return createBulkOps(mode, null);
}
private BulkOperations createBulkOps(BulkMode mode, Class<?> entityType) {
MongoPersistentEntity<?> entity = entityType != null
? operations.getConverter().getMappingContext().getPersistentEntity(entityType) : null;
BulkOperationContext bulkOperationContext = new BulkOperationContext(mode, entity,
new QueryMapper(operations.getConverter()), new UpdateMapper(operations.getConverter()));
DefaultBulkOperations operations = new DefaultBulkOperations(this.operations, mode, COLLECTION_NAME, null);
operations.setDefaultWriteConcern(WriteConcern.ACKNOWLEDGED);
DefaultBulkOperations bulkOps = new DefaultBulkOperations(operations, COLLECTION_NAME, bulkOperationContext);
bulkOps.setDefaultWriteConcern(WriteConcern.ACKNOWLEDGED);
bulkOps.setWriteConcernResolver(DefaultWriteConcernResolver.INSTANCE);
return operations;
return bulkOps;
}
private void insertSomeDocuments() {

Loading…
Cancel
Save