diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoDbUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoDbUtils.java
index 6cb6f766f..a1c9201a5 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoDbUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoDbUtils.java
@@ -18,18 +18,11 @@ package org.springframework.data.document.mongodb;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.springframework.dao.DataAccessException;
-import org.springframework.dao.DataAccessResourceFailureException;
-import org.springframework.dao.DataIntegrityViolationException;
-import org.springframework.data.document.UncategorizedDocumentStoreException;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
import com.mongodb.DB;
import com.mongodb.Mongo;
-import com.mongodb.MongoException;
-import com.mongodb.MongoException.DuplicateKey;
-import com.mongodb.MongoException.Network;
/**
* Helper class featuring helper methods for internal MongoDb classes.
@@ -52,36 +45,6 @@ abstract class MongoDbUtils {
private MongoDbUtils() {
}
-
- /**
- * Convert the given runtime exception to an appropriate exception from the
- * org.springframework.dao hierarchy.
- * Return null if no translation is appropriate: any other exception may
- * have resulted from user code, and should not be translated.
- * @param ex runtime exception that occurred
- * @return the corresponding DataAccessException instance,
- * or null if the exception should not be translated
- */
- public static DataAccessException translateMongoExceptionIfPossible(RuntimeException ex) {
-
- // Check for well-known MongoException subclasses.
-
- // All other MongoExceptions
- if(ex instanceof DuplicateKey) {
- return new DataIntegrityViolationException(ex.getMessage(),ex);
- }
- if(ex instanceof Network) {
- return new DataAccessResourceFailureException(ex.getMessage(), ex);
- }
- if (ex instanceof MongoException) {
- return new UncategorizedDocumentStoreException(ex.getMessage(), ex);
- }
-
- // If we get here, we have an exception that resulted from user code,
- // rather than the persistence provider, so we return null to indicate
- // that translation should not occur.
- return null;
- }
/**
* Obtains a {@link DB} connection for the given {@link Mongo} instance and database name
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoExceptionTranslator.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoExceptionTranslator.java
index 013876701..f92ac499f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoExceptionTranslator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoExceptionTranslator.java
@@ -16,24 +16,51 @@
package org.springframework.data.document.mongodb;
import org.springframework.dao.DataAccessException;
+import org.springframework.dao.DataAccessResourceFailureException;
+import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
+import org.springframework.data.document.UncategorizedDocumentStoreException;
+import com.mongodb.MongoException;
+import com.mongodb.MongoException.DuplicateKey;
+import com.mongodb.MongoException.Network;
/**
- * Simple {@link PersistenceExceptionTranslator} for Mongo.
+ * Simple {@link PersistenceExceptionTranslator} for Mongo. Convert the given runtime exception to an appropriate
+ * exception from the {@code org.springframework.dao} hierarchy. Return {@literal null} if no translation is
+ * appropriate: any other exception may have resulted from user code, and should not be translated.
+ * @param ex runtime exception that occurred
+ * @return the corresponding DataAccessException instance, or {@literal null} if the exception should not be translated
+ *
*
* @author Oliver Gierke
*/
public class MongoExceptionTranslator implements PersistenceExceptionTranslator {
- /*
- * (non-Javadoc)
- *
- * @see org.springframework.dao.support.PersistenceExceptionTranslator#
- * translateExceptionIfPossible(java.lang.RuntimeException)
- */
- public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.springframework.dao.support.PersistenceExceptionTranslator#
+ * translateExceptionIfPossible(java.lang.RuntimeException)
+ */
+ public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
- return MongoDbUtils.translateMongoExceptionIfPossible(ex);
- }
+ // Check for well-known MongoException subclasses.
+
+ // All other MongoExceptions
+ if (ex instanceof DuplicateKey) {
+ return new DataIntegrityViolationException(ex.getMessage(), ex);
+ }
+ if (ex instanceof Network) {
+ return new DataAccessResourceFailureException(ex.getMessage(), ex);
+ }
+ if (ex instanceof MongoException) {
+ return new UncategorizedDocumentStoreException(ex.getMessage(), ex);
+ }
+
+ // If we get here, we have an exception that resulted from user code,
+ // rather than the persistence provider, so we return null to indicate
+ // that translation should not occur.
+ return null;
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoFactoryBean.java
index 4fee05697..9346aaa55 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoFactoryBean.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoFactoryBean.java
@@ -22,8 +22,6 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
-import org.springframework.dao.DataAccessException;
-import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.util.Assert;
import com.mongodb.Mongo;
@@ -38,8 +36,7 @@ import com.mongodb.ServerAddress;
*
* @since 1.0
*/
-public class MongoFactoryBean implements FactoryBean, InitializingBean,
- PersistenceExceptionTranslator {
+public class MongoFactoryBean implements FactoryBean, InitializingBean {
/**
@@ -120,10 +117,4 @@ public class MongoFactoryBean implements FactoryBean, InitializingBean,
}
}
}
-
- public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
- logger.debug("Translating " + ex);
- return MongoDbUtils.translateMongoExceptionIfPossible(ex);
- }
-
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoOperations.java
index 9d6167929..4771cd624 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoOperations.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoOperations.java
@@ -16,6 +16,7 @@
package org.springframework.data.document.mongodb;
import java.util.List;
+import java.util.Set;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
@@ -27,7 +28,7 @@ import com.mongodb.DBObject;
*
* @author Thomas Risberg
* @author Mark Pollack
- *
+ * @author Oliver Gierke
*/
public interface MongoOperations {
@@ -38,7 +39,7 @@ public interface MongoOperations {
String getDefaultCollectionName();
/**
- * The default collection used by this template
+ * The default collection used by this template.
* @return The default collection used by this template
*/
DBCollection getDefaultCollection();
@@ -117,17 +118,17 @@ public interface MongoOperations {
DBCollection createCollection(String collectionName);
/**
- * Create a collect with the provided name and options
+ * Create a collect with the provided name and options.
* @param collectionName name of the collection
* @param collectionOptions options to use when creating the collection.
*/
void createCollection(String collectionName, CollectionOptions collectionOptions);
/**
- * A list of collection names
+ * A set of collection names.
* @return list of collection names
*/
- List getCollectionNames();
+ Set getCollectionNames();
/**
* Get a collection by name, creating it if it doesn't exist.
@@ -216,14 +217,14 @@ public interface MongoOperations {
*
* @param listToSave the list of objects to save.
*/
- void insertList(List listToSave);
+ void insertList(List extends Object> listToSave);
/**
* Insert a list of objects into the specified collection in a single batch write to the database.
* @param collectionName name of the collection to store the object in
* @param listToSave the list of objects to save.
*/
- void insertList(String collectionName, List listToSave);
+ void insertList(String collectionName, List extends Object> listToSave);
/**
* Insert a list of objects into the specified collection using the provided MongoWriter instance
@@ -233,7 +234,7 @@ public interface MongoOperations {
* @param listToSave the list of objects to save.
* @param writer the writer to convert the object to save into a DBObject
*/
- void insertList(String collectionName, List listToSave, MongoWriter writer);
+ void insertList(String collectionName, List extends T> listToSave, MongoWriter writer);
/**
* Save the object to the default collection. This will perform an insert if the object is not already
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java
index 343b4bc0f..031f9b515 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java
@@ -16,19 +16,20 @@
package org.springframework.data.document.mongodb;
-import static java.lang.String.*;
-
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.Set;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.factory.InitializingBean;
-import org.springframework.dao.DataRetrievalFailureException;
+import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
+import org.springframework.jca.cci.core.ConnectionCallback;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
@@ -39,7 +40,6 @@ import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.MongoException;
-import com.mongodb.WriteResult;
import com.mongodb.util.JSON;
/**
@@ -53,10 +53,10 @@ import com.mongodb.util.JSON;
public class MongoTemplate implements InitializingBean, MongoOperations {
private static final String ID = "_id";
- private static final String COLLECTION_ERROR_TEMPLATE = "Error creating collection %s: %s";
private final MongoConverter mongoConverter;
private final Mongo mongo;
+ private final MongoExceptionTranslator exceptionTranslator = new MongoExceptionTranslator();
private String defaultCollectionName;
private String databaseName;
@@ -98,7 +98,7 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
}
/**
- * Sets the password to use to authenticate with the Mongo database
+ * Sets the password to use to authenticate with the Mongo database.
*
* @param password The password to use
*/
@@ -107,10 +107,20 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
this.password = password;
}
+ /**
+ * Sets the name of the default collection to be used.
+ *
+ * @param defaultCollectionName
+ */
public void setDefaultCollectionName(String defaultCollectionName) {
this.defaultCollectionName = defaultCollectionName;
}
+ /**
+ * Sets the database name to be used.
+ *
+ * @param databaseName
+ */
public void setDatabaseName(String databaseName) {
Assert.notNull(databaseName);
this.databaseName = databaseName;
@@ -127,7 +137,12 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
* @see org.springframework.data.document.mongodb.MongoOperations#getDefaultCollection()
*/
public DBCollection getDefaultCollection() {
- return getDb().getCollection(getDefaultCollectionName());
+
+ return execute(new DBCallback() {
+ public DBCollection doInDB(DB db) throws MongoException, DataAccessException {
+ return db.getCollection(getDefaultCollectionName());
+ }
+ });
}
/* (non-Javadoc)
@@ -140,33 +155,41 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#executeCommand(com.mongodb.DBObject)
*/
- public void executeCommand(DBObject command) {
- CommandResult cr = getDb().command(command);
- String err = cr.getErrorMessage();
- if (err != null) {
- throw new InvalidDataAccessApiUsageException("Command execution of " +
- command.toString() + " failed: " + err);
+ public void executeCommand(final DBObject command) {
+
+ CommandResult result = execute(new DBCallback() {
+ public CommandResult doInDB(DB db) throws MongoException, DataAccessException {
+ return db.command(command);
+ }
+ });
+
+ String error = result.getErrorMessage();
+ if (error != null) {
+ throw new InvalidDataAccessApiUsageException("Command execution of " +
+ command.toString() + " failed: " + error);
}
}
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#execute(org.springframework.data.document.mongodb.DBCallback)
*/
- public T execute(DBCallback action) {
- DB db = getDb();
-
+ public T execute(DbCallback action) {
+
+ Assert.notNull(action);
+
try {
+ DB db = getDb();
return action.doInDB(db);
} catch (MongoException e) {
- throw MongoDbUtils.translateMongoExceptionIfPossible(e);
+ throw potentiallyConvertRuntimeException(e);
}
}
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#execute(org.springframework.data.document.mongodb.CollectionCallback)
*/
- public T execute(CollectionCallback action) {
- return execute(action, defaultCollectionName);
+ public T execute(CollectionCallback callback) {
+ return execute(callback, defaultCollectionName);
}
/* (non-Javadoc)
@@ -174,79 +197,126 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
*/
public T execute(CollectionCallback callback, String collectionName) {
+ Assert.notNull(callback);
+
try {
- return callback.doInCollection(getCollection(collectionName));
+ DBCollection collection = getDb().getCollection(collectionName);
+ return callback.doInCollection(collection);
} catch (MongoException e) {
- throw MongoDbUtils.translateMongoExceptionIfPossible(e);
+ throw potentiallyConvertRuntimeException(e);
+ }
+ }
+
+ /**
+ * Central callback executing method to do queries against the datastore that requires reading a collection of
+ * objects. It will take the following steps Execute the given {@link ConnectionCallback} for a
+ * {@link DBCursor}. Prepare that {@link DBCursor} with the given {@link CursorPreparer} (will be skipped
+ * if {@link CursorPreparer} is {@literal null} Iterate over the {@link DBCursor} and applies the given
+ * {@link DbObjectCallback} to each of the {@link DBObject}s collecting the actual result {@link List}.
+ *
+ * @param
+ * @param collectionCallback the callback to retrieve the {@link DBCursor} with
+ * @param preparer the {@link CursorPreparer} to potentially modify the {@link DBCursor} before ireating over it
+ * @param objectCallback the {@link DbObjectCallback} to transform {@link DBObject}s into the actual domain type
+ * @param collectionName the collection to be queried
+ * @return
+ */
+ private List executeEach(CollectionCallback collectionCallback, CursorPreparer preparer,
+ DbObjectCallback objectCallback, String collectionName) {
+
+ try {
+ DBCursor cursor = collectionCallback.doInCollection(getCollection(collectionName));
+
+ if (preparer != null) {
+ preparer.prepare(cursor);
+ }
+
+ List result = new ArrayList();
+
+ for (DBObject object : cursor) {
+ result.add(objectCallback.doWith(object));
+ }
+
+ return result;
+ } catch (MongoException e) {
+ throw potentiallyConvertRuntimeException(e);
}
}
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#executeInSession(org.springframework.data.document.mongodb.DBCallback)
*/
- public T executeInSession(DBCallback action) {
- DB db = getDb();
- db.requestStart();
- try {
- return action.doInDB(db);
- } catch (MongoException e) {
- throw MongoDbUtils.translateMongoExceptionIfPossible(e);
- } finally {
- db.requestDone();
- }
+ public T executeInSession(final DBCallback action) {
+
+ return execute(new DBCallback() {
+ public T doInDB(DB db) throws MongoException, DataAccessException {
+ try {
+ db.requestStart();
+ return action.doInDB(db);
+ } finally {
+ db.requestDone();
+ }
+ }
+ });
}
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#createCollection(java.lang.String)
*/
- public DBCollection createCollection(String collectionName) {
- try {
- return getDb().createCollection(collectionName, new BasicDBObject());
- } catch (MongoException e) {
- throw new InvalidDataAccessApiUsageException(format(COLLECTION_ERROR_TEMPLATE, collectionName, e.getMessage()), e);
- }
+ public DBCollection createCollection(final String collectionName) {
+ return execute(new DBCallback() {
+ public DBCollection doInDB(DB db) throws MongoException, DataAccessException {
+ return db.createCollection(collectionName, new BasicDBObject());
+ }
+ });
}
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#createCollection(java.lang.String, org.springframework.data.document.mongodb.CollectionOptions)
*/
- public void createCollection(String collectionName, CollectionOptions collectionOptions) {
- try {
- getDb().createCollection(collectionName, convertToDbObject(collectionOptions));
- } catch (MongoException e) {
- throw new InvalidDataAccessApiUsageException(format(COLLECTION_ERROR_TEMPLATE, collectionName, e.getMessage()), e);
- }
+ public void createCollection(final String collectionName, final CollectionOptions collectionOptions) {
+ execute(new DBCallback() {
+ public Void doInDB(DB db) throws MongoException, DataAccessException {
+ db.createCollection(collectionName, convertToDbObject(collectionOptions));
+ return null;
+ }
+ });
}
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#getCollection(java.lang.String)
*/
- public DBCollection getCollection(String collectionName) {
- try {
- return getDb().getCollection(collectionName);
- } catch (MongoException e) {
- throw new InvalidDataAccessApiUsageException(format(COLLECTION_ERROR_TEMPLATE, collectionName, e.getMessage()), e);
- }
+ public DBCollection getCollection(final String collectionName) {
+ return execute(new DBCallback() {
+ public DBCollection doInDB(DB db) throws MongoException, DataAccessException {
+ return db.getCollection(collectionName);
+ }
+ });
}
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#collectionExists(java.lang.String)
*/
- public boolean collectionExists(String collectionName) {
- try {
- return getDb().collectionExists(collectionName);
- } catch (MongoException e) {
- throw new InvalidDataAccessApiUsageException("Error creating collection " + collectionName + ": " + e.getMessage(), e);
- }
+ public boolean collectionExists(final String collectionName) {
+ return execute(new DBCallback() {
+ public Boolean doInDB(DB db) throws MongoException, DataAccessException {
+ return db.collectionExists(collectionName);
+ }
+ });
}
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#dropCollection(java.lang.String)
*/
public void dropCollection(String collectionName) {
- getDb().getCollection(collectionName)
- .drop();
+
+ execute(new CollectionCallback() {
+ public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException {
+ collection.drop();
+ return null;
+ }
+ }, collectionName);
}
@@ -287,21 +357,24 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#insertList(java.util.List)
*/
- public void insertList(List listToSave) {
+ public void insertList(List extends Object> listToSave) {
insertList(getRequiredDefaultCollectionName(), listToSave);
}
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#insertList(java.lang.String, java.util.List)
*/
- public void insertList(String collectionName, List listToSave) {
+ public void insertList(String collectionName, List extends Object> listToSave) {
insertList(collectionName, listToSave, this.mongoConverter);
}
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#insertList(java.lang.String, java.util.List, org.springframework.data.document.mongodb.MongoWriter)
*/
- public void insertList(String collectionName, List listToSave, MongoWriter writer) {
+ public void insertList(String collectionName, List extends T> listToSave, MongoWriter writer) {
+
+ Assert.notNull(writer);
+
List dbObjectList = new ArrayList();
for (T o : listToSave) {
BasicDBObject dbDoc = new BasicDBObject();
@@ -341,51 +414,56 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
}
- protected Object insertDBObject(String collectionName, DBObject dbDoc) {
- if (dbDoc.keySet().size() > 0 ) {
- WriteResult wr = null;
- try {
- wr = getDb().getCollection(collectionName).insert(dbDoc);
- return dbDoc.get(ID);
- } catch (MongoException e) {
- throw new DataRetrievalFailureException(wr.getLastError().getErrorMessage(), e);
- }
- }
- else {
+ protected Object insertDBObject(String collectionName, final DBObject dbDoc) {
+
+ if (dbDoc.keySet().isEmpty()) {
return null;
}
+
+ return execute(new CollectionCallback() {
+ public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
+ collection.insert(dbDoc);
+ return dbDoc.get(ID);
+ }
+ }, collectionName);
+ }
+
+
+
+
+ protected List insertDBObjectList(String collectionName, final List dbDocList) {
+
+ if (dbDocList.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ execute(new CollectionCallback() {
+ public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException {
+ collection.insert(dbDocList);
+ return null;
+ }
+ }, collectionName);
+
+ List ids = new ArrayList();
+ for (DBObject dbo : dbDocList) {
+ ids.add(dbo.get(ID));
+ }
+ return ids;
}
- protected List insertDBObjectList(String collectionName, List dbDocList) {
- if (!dbDocList.isEmpty()) {
- List ids = new ArrayList();
- WriteResult wr = null;
- try {
- wr = getDb().getCollection(collectionName).insert(dbDocList);
- for (DBObject dbo : dbDocList) {
- ids.add(dbo.get(ID));
- }
- return ids;
- } catch (MongoException e) {
- throw new DataRetrievalFailureException(wr.getLastError().getErrorMessage(), e);
- }
- } else {
+ protected Object saveDBObject(String collectionName, final DBObject dbDoc) {
+
+ if (dbDoc.keySet().isEmpty()) {
return null;
}
- }
+
+ return execute(new CollectionCallback() {
- protected Object saveDBObject(String collectionName, DBObject dbDoc) {
- if (dbDoc.keySet().size() > 0 ) {
- WriteResult wr = null;
- try {
- wr = getDb().getCollection(collectionName).save(dbDoc);
+ public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
+ collection.save(dbDoc);
return dbDoc.get(ID);
- } catch (MongoException e) {
- throw new DataRetrievalFailureException(wr.getLastError().getErrorMessage(), e);
}
- } else {
- return null;
- }
+ }, collectionName);
}
/* (non-Javadoc)
@@ -398,13 +476,13 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#updateFirst(java.lang.String, com.mongodb.DBObject, com.mongodb.DBObject)
*/
- public void updateFirst(String collectionName, DBObject queryDoc, DBObject updateDoc) {
-
- try {
- getDb().getCollection(collectionName).update(queryDoc, updateDoc);
- } catch (MongoException e) {
- throw new DataRetrievalFailureException("Error during update using " + queryDoc + ", " + updateDoc + "!", e);
- }
+ public void updateFirst(String collectionName, final DBObject queryDoc, final DBObject updateDoc) {
+ execute(new CollectionCallback() {
+ public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException {
+ collection.update(queryDoc, updateDoc);
+ return null;
+ }
+ }, collectionName);
}
/* (non-Javadoc)
@@ -417,12 +495,13 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#updateMulti(java.lang.String, com.mongodb.DBObject, com.mongodb.DBObject)
*/
- public void updateMulti(String collectionName, DBObject queryDoc, DBObject updateDoc) {
- try {
- getDb().getCollection(collectionName).updateMulti(queryDoc, updateDoc);
- } catch (MongoException e) {
- throw new DataRetrievalFailureException("Error during updateMulti using " + queryDoc + ", " + updateDoc + "!", e);
- }
+ public void updateMulti(String collectionName, final DBObject queryDoc, final DBObject updateDoc) {
+ execute(new CollectionCallback() {
+ public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException {
+ collection.updateMulti(queryDoc, updateDoc);
+ return null;
+ }
+ }, collectionName);
}
/* (non-Javadoc)
@@ -435,12 +514,13 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#remove(java.lang.String, com.mongodb.DBObject)
*/
- public void remove(String collectionName, DBObject queryDoc) {
- try {
- getDb().getCollection(collectionName).remove(queryDoc);
- } catch (MongoException e) {
- throw new DataRetrievalFailureException("Error during remove using " + queryDoc + "!", e);
- }
+ public void remove(String collectionName, final DBObject queryDoc) {
+ execute(new CollectionCallback() {
+ public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException {
+ collection.remove(queryDoc);
+ return null;
+ }
+ }, collectionName);
}
@@ -448,53 +528,35 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
* @see org.springframework.data.document.mongodb.MongoOperations#getCollection(java.lang.Class)
*/
public List getCollection(Class targetClass) {
-
- List results = new ArrayList();
- DBCollection collection = getDb().getCollection(getDefaultCollectionName());
- for (DBObject dbo : collection.find()) {
- Object obj = mongoConverter.read(targetClass, dbo);
- //effectively acts as a query on the collection restricting it to elements of a specific type
- if (targetClass.isInstance(obj)) {
- results.add(targetClass.cast(obj));
- }
- }
- return results;
+ return executeEach(new FindCallback(null), null, new ReadDbObjectCallback(mongoConverter, targetClass),
+ getDefaultCollectionName());
}
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#getCollection(java.lang.String, java.lang.Class)
*/
public List getCollection(String collectionName, Class targetClass) {
-
- List results = new ArrayList();
- DBCollection collection = getDb().getCollection(collectionName);
- for (DBObject dbo : collection.find()) {
- Object obj = mongoConverter.read(targetClass, dbo);
- //effectively acts as a query on the collection restricting it to elements of a specific type
- if (targetClass.isInstance(obj)) {
- results.add(targetClass.cast(obj));
- }
- }
- return results;
+ return executeEach(new FindCallback(null), null, new ReadDbObjectCallback(mongoConverter, targetClass),
+ collectionName);
}
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#getCollectionNames()
*/
- public List getCollectionNames() {
- return new ArrayList(getDb().getCollectionNames());
+ public Set getCollectionNames() {
+ return execute(new DBCallback>() {
+ public Set doInDB(DB db) throws MongoException, DataAccessException {
+ return db.getCollectionNames();
+ }
+ });
}
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#getCollection(java.lang.String, java.lang.Class, org.springframework.data.document.mongodb.MongoReader)
*/
- public List getCollection(String collectionName, Class targetClass, MongoReader reader) {
- List results = new ArrayList();
- DBCollection collection = getDb().getCollection(collectionName);
- for (DBObject dbo : collection.find()) {
- results.add(reader.read(targetClass, dbo));
- }
- return results;
+ public List getCollection(String collectionName, Class targetClass, MongoReader reader) {
+ return executeEach(new FindCallback(null), null, new ReadDbObjectCallback(reader, targetClass),
+ collectionName);
}
// Queries that take JavaScript to express the query.
@@ -504,7 +566,7 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
*/
public List queryUsingJavaScript(String query, Class targetClass) {
return query(getDefaultCollectionName(), (DBObject)JSON.parse(query), targetClass); //
- }
+ }
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#queryUsingJavaScript(java.lang.String, java.lang.Class, org.springframework.data.document.mongodb.MongoReader)
@@ -554,7 +616,7 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#query(java.lang.String, com.mongodb.DBObject, java.lang.Class)
*/
- public List query(String collectionName, DBObject query, Class targetClass) {
+ public List query(String collectionName, DBObject query, Class targetClass) {
return query(collectionName, query, targetClass, (CursorPreparer) null);
}
@@ -562,37 +624,18 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
* @see org.springframework.data.document.mongodb.MongoOperations#query(java.lang.String, com.mongodb.DBObject, java.lang.Class, org.springframework.data.document.mongodb.CursorPreparer)
*/
public List query(String collectionName, DBObject query, Class targetClass, CursorPreparer preparer) {
- DBCollection collection = getDb().getCollection(collectionName);
- List results = new ArrayList();
- DBCursor cursor = collection.find(query);
- if (preparer != null) {
- preparer.prepare(cursor);
- }
- for (DBObject dbo : cursor) {
- Object obj = mongoConverter.read(targetClass,dbo);
- //effectively acts as a query on the collection restricting it to elements of a specific type
- if (targetClass.isInstance(obj)) {
- results.add(targetClass.cast(obj));
- }
- }
- return results;
+ return executeEach(new FindCallback(query), preparer, new ReadDbObjectCallback(mongoConverter, targetClass),
+ collectionName);
}
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoOperations#query(java.lang.String, com.mongodb.DBObject, java.lang.Class, org.springframework.data.document.mongodb.MongoReader)
*/
public List query(String collectionName, DBObject query, Class targetClass, MongoReader reader) {
- DBCollection collection = getDb().getCollection(collectionName);
- List results = new ArrayList();
- for (DBObject dbo : collection.find(query)) {
- results.add(reader.read(targetClass, dbo));
- }
- return results;
+ return executeEach(new FindCallback(query), null, new ReadDbObjectCallback(reader, targetClass),
+ collectionName);
}
- public RuntimeException convertMongoAccessException(RuntimeException ex) {
- return MongoDbUtils.translateMongoExceptionIfPossible(ex);
- }
public DB getDb() {
return MongoDbUtils.getDB(mongo, databaseName, username, password == null ? null : password.toCharArray());
@@ -603,7 +646,7 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
DBObject dbo = new BasicDBObject();
if (collectionOptions != null) {
if (collectionOptions.getCapped() != null) {
- dbo.put("capped", collectionOptions.getCapped().booleanValue());
+ dbo.put("capped", collectionOptions.getCapped().booleanValue());
}
if (collectionOptions.getSize() != null) {
dbo.put("size", collectionOptions.getSize().intValue());
@@ -634,6 +677,19 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
}
}
}
+
+ /**
+ * Tries to convert the given {@link RuntimeException} into a {@link DataAccessException} but returns the original
+ * exception if the conversation failed. Thus allows safe rethrowing of the return value.
+ *
+ * @param ex
+ * @return
+ */
+ private RuntimeException potentiallyConvertRuntimeException(RuntimeException ex) {
+
+ RuntimeException resolved = this.exceptionTranslator.translateExceptionIfPossible(ex);
+ return resolved == null ? ex : resolved;
+ }
/*
* (non-Javadoc)
@@ -641,10 +697,62 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
*/
public void afterPropertiesSet() {
if (this.getDefaultCollectionName() != null) {
- DB db = getDb();
- if (! db.collectionExists(getDefaultCollectionName())) {
- db.createCollection(getDefaultCollectionName(), null);
+ if (!collectionExists(getDefaultCollectionName())) {
+ createCollection(getDefaultCollectionName(), null);
}
}
}
+
+
+ /**
+ * Simple {@link CollectionCallback} that takes a query {@link DBObject} and executes that against the
+ * {@link DBCollection}.
+ *
+ * @author Oliver Gierke
+ */
+ private static class FindCallback implements CollectionCallback {
+
+ private final DBObject query;
+
+ public FindCallback(DBObject query) {
+ this.query = query;
+ }
+
+ public DBCursor doInCollection(DBCollection collection) throws MongoException, DataAccessException {
+ return collection.find(query);
+ }
+ }
+
+ /**
+ * Simple internal callback to allow operations on a {@link DBObject}.
+ *
+ * @author Oliver Gierke
+ */
+
+ private interface DbObjectCallback {
+
+ T doWith(DBObject object);
+ }
+
+ /**
+ * Simple {@link DbObjectCallback} that will transform {@link DBObject} into the given target type using the given
+ * {@link MongoReader}.
+ *
+ * @author Oliver Gierke
+ */
+ private static class ReadDbObjectCallback implements DbObjectCallback {
+
+ private final MongoReader super T> reader;
+ private final Class type;
+
+ public ReadDbObjectCallback(MongoReader super T> reader, Class type) {
+ this.reader = reader;
+ this.type = type;
+ }
+
+ @SuppressWarnings("unchecked")
+ public T doWith(DBObject object) {
+ return (T) reader.read(type, object);
+ }
+ }
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoOperationsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoOperationsUnitTests.java
new file mode 100644
index 000000000..c75f2e90a
--- /dev/null
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoOperationsUnitTests.java
@@ -0,0 +1,344 @@
+/*
+ * 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.document.mongodb;
+
+import static org.junit.Assert.*;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.springframework.dao.DataAccessException;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBObject;
+
+/**
+ * Abstract base class for unit tests to specify behaviour we expect from {@link MongoOperations}. Subclasses return
+ * instances of their implementation and thus can see if it correctly implements the {@link MongoOperations} interface.
+ *
+ * @author Oliver Gierke
+ */
+@RunWith(MockitoJUnitRunner.class)
+public abstract class MongoOperationsUnitTests {
+
+ @Mock
+ CollectionCallback collectionCallback;
+ @Mock
+ DbCallback dbCallback;
+
+ MongoConverter converter;
+ Person person;
+ List persons;
+
+ @Before
+ public final void operationsSetUp() {
+
+ person = new Person("Oliver");
+ persons = Arrays.asList(person);
+
+ converter = new MongoConverter() {
+
+ public Object read(Class extends Object> clazz, DBObject dbo) {
+ return person;
+ }
+
+ public void write(Object t, DBObject dbo) {
+ dbo.put("firstName", person.getFirstName());
+ }
+ };
+ }
+
+
+ @Test(expected = IllegalArgumentException.class)
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public void rejectsNullForCollectionCallback() {
+
+ getOperations().execute((CollectionCallback) null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public void rejectsNullForCollectionCallback2() {
+ getOperations().execute((CollectionCallback) null, "collection");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public void rejectsNullForDbCallback() {
+ getOperations().execute((DbCallback) null);
+ }
+
+ @Test
+ public void convertsExceptionForCollectionExists() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.collectionExists("foo");
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForCreateCollection() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.createCollection("foo");
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForCreateCollection2() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.createCollection("foo", new CollectionOptions(1, 1, true));
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForDropCollection() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.dropCollection("foo");
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForExecuteCollectionCallback() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.execute(collectionCallback);
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForExecuteDbCallback() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.execute(dbCallback);
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForExecuteCollectionCallbackAndCollection() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.execute(collectionCallback, "collection");
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForExecuteCommand() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.executeCommand(new BasicDBObject());
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForExecuteStringCommand() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.executeCommand("");
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForExecuteInSession() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.executeInSession(dbCallback);
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForGetCollection() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.getCollection(Object.class);
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForGetCollectionWithCollectionName() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.getCollection("collection");
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForGetCollectionWithCollectionNameAndType() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.getCollection("collection", Object.class);
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForGetCollectionWithCollectionNameTypeAndReader() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.getCollection("collection", Object.class, converter);
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForGetCollectionNames() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.getCollectionNames();
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForGetDefaultCollection() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.getDefaultCollection();
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForInsert() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.insert(person);
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForInsert2() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.insert("collection", person);
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForInsert3() {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.insert("collection", person, converter);
+ }
+ }.assertDataAccessException();
+ }
+
+
+ @Test
+ public void convertsExceptionForInsertList() throws Exception {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.insertList(persons);
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForGetInsertList2() throws Exception {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.insertList("collection", persons);
+ }
+ }.assertDataAccessException();
+ }
+
+ @Test
+ public void convertsExceptionForGetInsertList3() throws Exception {
+ new Execution() {
+ @Override
+ public void doWith(MongoOperations operations) {
+ operations.insertList("collection", persons, converter);
+ }
+ }.assertDataAccessException();
+ }
+
+ private abstract class Execution {
+
+ public void assertDataAccessException() {
+ assertException(DataAccessException.class);
+ }
+
+ public void assertException(Class extends Exception> exception) {
+
+ try {
+ doWith(getOperationsForExceptionHandling());
+ fail("Expected " + exception + " but completed without any!");
+ } catch (Exception e) {
+ assertTrue("Expected " + exception + " but got " + e, exception.isInstance(e));
+ }
+ }
+
+ public abstract void doWith(MongoOperations operations);
+ }
+
+ /**
+ * Expects an {@link MongoOperations} instance that will be used to check that invoking methods on it will only
+ * cause {@link DataAccessException}s.
+ *
+ * @return
+ */
+ protected abstract MongoOperations getOperationsForExceptionHandling();
+
+ /**
+ * Returns a plain {@link MongoOperations}.
+ *
+ * @return
+ */
+ protected abstract MongoOperations getOperations();
+}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoTemplateUnitTests.java
index b9b0e45a6..b3b0814e6 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoTemplateUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoTemplateUnitTests.java
@@ -23,7 +23,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
-import org.springframework.dao.DataRetrievalFailureException;
+import org.springframework.dao.DataAccessException;
import org.springframework.test.util.ReflectionTestUtils;
import com.mongodb.DB;
@@ -36,7 +36,7 @@ import com.mongodb.MongoException;
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
-public class MongoTemplateUnitTests {
+public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
MongoTemplate template;
@@ -48,7 +48,7 @@ public class MongoTemplateUnitTests {
@Before
public void setUp() {
- this.template = new MongoTemplate(mongo, "database");
+ this.template = new MongoTemplate(mongo, "database", "default");
}
@Test(expected = IllegalArgumentException.class)
@@ -61,7 +61,7 @@ public class MongoTemplateUnitTests {
new MongoTemplate(null, "database");
}
- @Test(expected = DataRetrievalFailureException.class)
+ @Test(expected = DataAccessException.class)
public void removeHandlesMongoExceptionProperly() throws Exception {
MongoTemplate template = mockOutGetDb();
when(db.getCollection("collection")).thenThrow(new MongoException("Exception!"));
@@ -87,4 +87,22 @@ public class MongoTemplateUnitTests {
stub(template.getDb()).toReturn(db);
return template;
}
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.document.mongodb.MongoOperationsUnitTests#getOperations()
+ */
+ @Override
+ protected MongoOperations getOperationsForExceptionHandling() {
+ MongoTemplate template = spy(this.template);
+ stub(template.getDb()).toThrow(new MongoException("Error!"));
+ return template;
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.document.mongodb.MongoOperationsUnitTests#getOperations()
+ */
+ @Override
+ protected MongoOperations getOperations() {
+ return this.template;
+ }
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/Person.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/Person.java
index 01b134f64..a3b86c433 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/Person.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/Person.java
@@ -20,6 +20,14 @@ public class Person {
private String firstName;
private Person friend;
+
+ public Person() {
+
+ }
+
+ public Person(String firstname) {
+ this.firstName = firstname;
+ }
public String getFirstName() {