DATAMONGO-213 - Add WriteConcern to arguments of MongoOperations.update*() methods
DATAMONGO-320 - Remove use of slaveOk boolean option in MongoTemplate as it is deprecated. Replace with ReadPreference
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2011 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.WriteConcern;
|
||||
|
||||
/**
|
||||
* Represents an action taken against the collection. Used by {@link WriteConcernResolver} to determine a custom
|
||||
* WriteConcern based on this information.
|
||||
*
|
||||
* Properties that will always be not-null are collectionName and defaultWriteConcern.
|
||||
* The EntityClass is null only for the MongoActionOperaton.INSERT_LIST.
|
||||
*
|
||||
* INSERT, SAVE have null query,
|
||||
* REMOVE has null document
|
||||
* INSERT_LIST has null entityClass, document, and query.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*
|
||||
*/
|
||||
public class MongoAction {
|
||||
|
||||
private String collectionName;
|
||||
|
||||
private WriteConcern defaultWriteConcern;
|
||||
|
||||
private Class<?> entityClass;
|
||||
|
||||
private MongoActionOperation mongoActionOperation;
|
||||
|
||||
private DBObject query;
|
||||
|
||||
private DBObject document;
|
||||
|
||||
/**
|
||||
* Create an instance of a MongoAction
|
||||
* @param defaultWriteConcern the default write concern
|
||||
* @param mongoActionOperation action being taken against the collection
|
||||
* @param collectionName the collection name
|
||||
* @param entityClass the POJO that is being operated against
|
||||
* @param document the converted DBObject from the POJO or Spring Update object
|
||||
* @param query the converted DBOjbect from the Spring Query object
|
||||
*/
|
||||
public MongoAction(WriteConcern defaultWriteConcern, MongoActionOperation mongoActionOperation,
|
||||
String collectionName, Class<?> entityClass, DBObject document, DBObject query) {
|
||||
super();
|
||||
this.defaultWriteConcern = defaultWriteConcern;
|
||||
this.mongoActionOperation = mongoActionOperation;
|
||||
this.collectionName = collectionName;
|
||||
this.entityClass = entityClass;
|
||||
this.query = query;
|
||||
this.document = document;
|
||||
}
|
||||
|
||||
public String getCollectionName() {
|
||||
return collectionName;
|
||||
}
|
||||
|
||||
public WriteConcern getDefaultWriteConcern() {
|
||||
return defaultWriteConcern;
|
||||
}
|
||||
|
||||
public Class<?> getEntityClass() {
|
||||
return entityClass;
|
||||
}
|
||||
|
||||
public MongoActionOperation getMongoActionOperation() {
|
||||
return mongoActionOperation;
|
||||
}
|
||||
|
||||
public DBObject getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public DBObject getDocument() {
|
||||
return document;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under t
|
||||
import org.springframework.data.convert.EntityReader;
|
||||
he Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
/**
|
||||
* Enumeration for operations on a collection. Used with {@link MongoAction} to help determine the
|
||||
* WriteConcern to use for a given mutating operation
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @see MongoAction
|
||||
*
|
||||
*/
|
||||
public enum MongoActionOperation {
|
||||
|
||||
REMOVE,
|
||||
UPDATE,
|
||||
INSERT,
|
||||
INSERT_LIST,
|
||||
SAVE
|
||||
}
|
||||
@@ -41,6 +41,7 @@ import com.mongodb.MapReduceCommand;
|
||||
import com.mongodb.MapReduceOutput;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoException;
|
||||
import com.mongodb.ReadPreference;
|
||||
import com.mongodb.WriteConcern;
|
||||
import com.mongodb.WriteResult;
|
||||
import com.mongodb.util.JSON;
|
||||
@@ -124,6 +125,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
* the DB or Collection.
|
||||
*/
|
||||
private WriteConcern writeConcern = null;
|
||||
|
||||
private WriteConcernResolver writeConcernResolver = new DefaultWriteConcernResolver();
|
||||
|
||||
/*
|
||||
* WriteResultChecking to be used for write operations if it has been
|
||||
@@ -131,10 +134,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
*/
|
||||
private WriteResultChecking writeResultChecking = WriteResultChecking.NONE;
|
||||
|
||||
/*
|
||||
* Flag used to indicate use of slaveOk() for any operations on collections.
|
||||
/**
|
||||
* Set the ReadPreference when operating on a collection. See {@link #prepareCollection(DBCollection)}
|
||||
*/
|
||||
private boolean slaveOk = false;
|
||||
private ReadPreference readPreference = null;
|
||||
|
||||
private final MongoConverter mongoConverter;
|
||||
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
|
||||
@@ -223,12 +226,20 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: document properly
|
||||
* Configures the {@link WriteConcernResolver} to be used with the template.
|
||||
*
|
||||
* @param slaveOk
|
||||
* @param writeConcernResolver
|
||||
*/
|
||||
public void setSlaveOk(boolean slaveOk) {
|
||||
this.slaveOk = slaveOk;
|
||||
public void setWriteConcernResolver(WriteConcernResolver writeConcernResolver) {
|
||||
this.writeConcernResolver = writeConcernResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by @{link {@link #prepareCollection(DBCollection)} to set the {@link ReadPreference} before any operations are performed.
|
||||
* @param readPreference
|
||||
*/
|
||||
public void setReadPreference(ReadPreference readPreference) {
|
||||
this.readPreference = readPreference;
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
@@ -578,8 +589,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
* @param collection
|
||||
*/
|
||||
protected void prepareCollection(DBCollection collection) {
|
||||
if (this.slaveOk) {
|
||||
collection.slaveOk();
|
||||
if (this.readPreference != null) {
|
||||
collection.setReadPreference(readPreference);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -590,8 +601,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
* @param writeConcern any WriteConcern already configured or null
|
||||
* @return The prepared WriteConcern or null
|
||||
*/
|
||||
protected WriteConcern prepareWriteConcern(WriteConcern writeConcern) {
|
||||
return writeConcern;
|
||||
protected WriteConcern prepareWriteConcern(MongoAction mongoAction) {
|
||||
return writeConcernResolver.resolve(mongoAction);
|
||||
}
|
||||
|
||||
protected <T> void doInsert(String collectionName, T objectToSave, MongoWriter<T> writer) {
|
||||
@@ -601,7 +612,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
writer.write(objectToSave, dbDoc);
|
||||
|
||||
maybeEmitEvent(new BeforeSaveEvent<T>(objectToSave, dbDoc));
|
||||
Object id = insertDBObject(collectionName, dbDoc);
|
||||
Object id = insertDBObject(collectionName, dbDoc, objectToSave.getClass());
|
||||
|
||||
populateIdIfNecessary(objectToSave, id);
|
||||
maybeEmitEvent(new AfterSaveEvent<T>(objectToSave, dbDoc));
|
||||
@@ -685,19 +696,20 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
writer.write(objectToSave, dbDoc);
|
||||
|
||||
maybeEmitEvent(new BeforeSaveEvent<T>(objectToSave, dbDoc));
|
||||
Object id = saveDBObject(collectionName, dbDoc);
|
||||
Object id = saveDBObject(collectionName, dbDoc, objectToSave.getClass());
|
||||
|
||||
populateIdIfNecessary(objectToSave, id);
|
||||
maybeEmitEvent(new AfterSaveEvent<T>(objectToSave, dbDoc));
|
||||
}
|
||||
|
||||
protected Object insertDBObject(String collectionName, final DBObject dbDoc) {
|
||||
protected Object insertDBObject(final String collectionName, final DBObject dbDoc, final Class<?> entityClass) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("insert DBObject containing fields: " + dbDoc.keySet() + " in collection: " + collectionName);
|
||||
}
|
||||
return execute(collectionName, new CollectionCallback<Object>() {
|
||||
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
WriteConcern writeConcernToUse = prepareWriteConcern(writeConcern);
|
||||
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT, collectionName, entityClass, dbDoc, null);
|
||||
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
|
||||
if (writeConcernToUse == null) {
|
||||
collection.insert(dbDoc);
|
||||
} else {
|
||||
@@ -708,7 +720,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
});
|
||||
}
|
||||
|
||||
protected List<ObjectId> insertDBObjectList(String collectionName, final List<DBObject> dbDocList) {
|
||||
protected List<ObjectId> insertDBObjectList(final String collectionName, final List<DBObject> dbDocList) {
|
||||
if (dbDocList.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -718,7 +730,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
}
|
||||
execute(collectionName, new CollectionCallback<Void>() {
|
||||
public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
WriteConcern writeConcernToUse = prepareWriteConcern(writeConcern);
|
||||
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT_LIST, collectionName, null, null, null);
|
||||
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
|
||||
if (writeConcernToUse == null) {
|
||||
collection.insert(dbDocList);
|
||||
} else {
|
||||
@@ -741,13 +754,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
return ids;
|
||||
}
|
||||
|
||||
protected Object saveDBObject(String collectionName, final DBObject dbDoc) {
|
||||
protected Object saveDBObject(final String collectionName, final DBObject dbDoc, final Class<?> entityClass) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("save DBObject containing fields: " + dbDoc.keySet());
|
||||
}
|
||||
return execute(collectionName, new CollectionCallback<Object>() {
|
||||
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
WriteConcern writeConcernToUse = prepareWriteConcern(writeConcern);
|
||||
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.SAVE, collectionName, entityClass, dbDoc, null);
|
||||
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
|
||||
if (writeConcernToUse == null) {
|
||||
collection.save(dbDoc);
|
||||
} else {
|
||||
@@ -796,7 +810,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
}
|
||||
|
||||
WriteResult wr;
|
||||
WriteConcern writeConcernToUse = prepareWriteConcern(writeConcern);
|
||||
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.UPDATE, collectionName, entityClass, updateObj, queryObj);
|
||||
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
|
||||
if (writeConcernToUse == null) {
|
||||
if (multi) {
|
||||
wr = collection.updateMulti(queryObj, updateObj);
|
||||
@@ -869,7 +884,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
doRemove(determineCollectionName(entityClass), query, entityClass);
|
||||
}
|
||||
|
||||
protected <T> void doRemove(String collectionName, final Query query, Class<T> entityClass) {
|
||||
protected <T> void doRemove(final String collectionName, final Query query, final Class<T> entityClass) {
|
||||
if (query == null) {
|
||||
throw new InvalidDataAccessApiUsageException("Query passed in to remove can't be null");
|
||||
}
|
||||
@@ -879,7 +894,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
DBObject dboq = mapper.getMappedObject(queryObject, entity);
|
||||
WriteResult wr = null;
|
||||
WriteConcern writeConcernToUse = prepareWriteConcern(writeConcern);
|
||||
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.REMOVE, collectionName, entityClass, null, queryObject);
|
||||
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("remove using query: " + queryObject + " in collection: " + collection.getName());
|
||||
}
|
||||
@@ -1412,8 +1428,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
/**
|
||||
* Checks and handles any errors.
|
||||
* <p/>
|
||||
* TODO: current implementation logs errors - will be configurable to log warning, errors or throw exception in later
|
||||
* versions
|
||||
* Current implementation logs errors. Future version may make this configurable to log warning, errors or throw exception.
|
||||
*/
|
||||
private void handleAnyWriteResultErrors(WriteResult wr, DBObject query, String operation) {
|
||||
if (WriteResultChecking.NONE == this.writeResultChecking) {
|
||||
@@ -1587,6 +1602,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
private class DefaultWriteConcernResolver implements WriteConcernResolver {
|
||||
|
||||
public WriteConcern resolve(MongoAction action) {
|
||||
return action.getDefaultWriteConcern();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link DbObjectCallback} that assumes a {@link GeoResult} to be created, delegates actual content unmarshalling to
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2011 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import com.mongodb.WriteConcern;
|
||||
|
||||
/**
|
||||
* A strategy interface to determine the WriteConcern to use for a given MongoDbAction.
|
||||
*
|
||||
* Return the passed in default WriteConcern (a property on MongoAction) if no determination can be made.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*
|
||||
*/
|
||||
public interface WriteConcernResolver {
|
||||
|
||||
/**
|
||||
* Resolve the WriteConcern given the MongoAction
|
||||
* @param action describes the context of the Mongo action. Contains a default WriteConcern to use if one should not be resolved.
|
||||
* @return a WriteConcern based on the passed in MongoAction value, maybe null
|
||||
*/
|
||||
WriteConcern resolve(MongoAction action);
|
||||
}
|
||||
@@ -61,6 +61,8 @@ import com.mongodb.DBObject;
|
||||
import com.mongodb.DBRef;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoException;
|
||||
import com.mongodb.ReadPreference;
|
||||
import com.mongodb.WriteConcern;
|
||||
import com.mongodb.WriteResult;
|
||||
|
||||
/**
|
||||
@@ -774,8 +776,8 @@ public class MongoTemplateTests {
|
||||
|
||||
|
||||
@Test
|
||||
public void testUsingSlaveOk() throws Exception {
|
||||
this.template.execute("slaveOkTest", new CollectionCallback<Object>() {
|
||||
public void testUsingReadPreference() throws Exception {
|
||||
this.template.execute("readPref", new CollectionCallback<Object>() {
|
||||
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
assertThat(collection.getOptions(), is(0));
|
||||
assertThat(collection.getDB().getOptions(), is(0));
|
||||
@@ -783,10 +785,10 @@ public class MongoTemplateTests {
|
||||
}
|
||||
});
|
||||
MongoTemplate slaveTemplate = new MongoTemplate(factory);
|
||||
slaveTemplate.setSlaveOk(true);
|
||||
slaveTemplate.execute("slaveOkTest", new CollectionCallback<Object>() {
|
||||
slaveTemplate.setReadPreference(ReadPreference.SECONDARY);
|
||||
slaveTemplate.execute("readPref", new CollectionCallback<Object>() {
|
||||
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
assertThat(collection.getOptions(), is(4));
|
||||
assertThat(collection.getReadPreference(), is(ReadPreference.SECONDARY));
|
||||
assertThat(collection.getDB().getOptions(), is(0));
|
||||
return null;
|
||||
}
|
||||
@@ -821,6 +823,55 @@ public class MongoTemplateTests {
|
||||
assertThat(result.getId(), is(person.getId()));
|
||||
assertThat(result.getFirstName(), is("Carter"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteConcernResolver() {
|
||||
|
||||
PersonWithIdPropertyOfTypeObjectId person = new PersonWithIdPropertyOfTypeObjectId();
|
||||
person.setId(new ObjectId());
|
||||
person.setFirstName("Dave");
|
||||
|
||||
template.setWriteConcern(WriteConcern.NONE);
|
||||
template.save(person);
|
||||
WriteResult result = template.updateFirst(query(where("id").is(person.getId())), update("firstName", "Carter"),
|
||||
PersonWithIdPropertyOfTypeObjectId.class);
|
||||
WriteConcern lastWriteConcern = result.getLastConcern();
|
||||
assertThat(lastWriteConcern, equalTo(WriteConcern.NONE));
|
||||
|
||||
FsyncSafeWriteConcernResolver resolver = new FsyncSafeWriteConcernResolver();
|
||||
template.setWriteConcernResolver(resolver);
|
||||
Query q = query(where("_id").is(person.getId()));
|
||||
Update u = update("firstName", "Carter");
|
||||
result = template.updateFirst(q, u, PersonWithIdPropertyOfTypeObjectId.class);
|
||||
lastWriteConcern = result.getLastConcern();
|
||||
assertThat(lastWriteConcern, equalTo(WriteConcern.FSYNC_SAFE));
|
||||
|
||||
MongoAction lastMongoAction = resolver.getMongoAction();
|
||||
assertThat(lastMongoAction.getCollectionName(), is("personWithIdPropertyOfTypeObjectId"));
|
||||
assertThat(lastMongoAction.getDefaultWriteConcern(), equalTo(WriteConcern.NONE));
|
||||
assertThat(lastMongoAction.getDocument(), notNullValue());
|
||||
assertThat(lastMongoAction.getEntityClass().toString(), is(PersonWithIdPropertyOfTypeObjectId.class.toString()));
|
||||
assertThat(lastMongoAction.getMongoActionOperation(), is(MongoActionOperation.UPDATE));
|
||||
assertThat(lastMongoAction.getQuery(), equalTo(q.getQueryObject()));
|
||||
assertThat(lastMongoAction.getDocument(), equalTo(u.getUpdateObject()));
|
||||
|
||||
}
|
||||
|
||||
private class FsyncSafeWriteConcernResolver implements WriteConcernResolver {
|
||||
|
||||
private MongoAction mongoAction;
|
||||
|
||||
public WriteConcern resolve(MongoAction action) {
|
||||
this.mongoAction = action;
|
||||
return WriteConcern.FSYNC_SAFE;
|
||||
}
|
||||
|
||||
public MongoAction getMongoAction() {
|
||||
return mongoAction;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see DATADOC-246
|
||||
|
||||
Reference in New Issue
Block a user