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 5a3fa9660..e427962cc 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 @@ -109,6 +109,7 @@ import com.mongodb.util.JSON; * @author Graeme Rocher * @author Mark Pollack * @author Oliver Gierke + * @author Amol Nayak */ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -734,11 +735,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT, collectionName, entityClass, dbDoc, null); WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction); + WriteResult wr; if (writeConcernToUse == null) { - collection.insert(dbDoc); + wr = collection.insert(dbDoc); } else { - collection.insert(dbDoc, writeConcernToUse); + wr = collection.insert(dbDoc, writeConcernToUse); } + handleAnyWriteResultErrors(wr, dbDoc, "insert"); return dbDoc.get(ID); } }); @@ -757,11 +760,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT_LIST, collectionName, null, null, null); WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction); + WriteResult wr; if (writeConcernToUse == null) { - collection.insert(dbDocList); + wr = collection.insert(dbDocList); } else { - collection.insert(dbDocList.toArray((DBObject[]) new BasicDBObject[dbDocList.size()]), writeConcernToUse); + wr = collection.insert(dbDocList.toArray((DBObject[]) new BasicDBObject[dbDocList.size()]), writeConcernToUse); } + handleAnyWriteResultErrors(wr, null, "insert_list"); return null; } }); @@ -788,11 +793,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.SAVE, collectionName, entityClass, dbDoc, null); WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction); + WriteResult wr; if (writeConcernToUse == null) { - collection.save(dbDoc); + wr = collection.save(dbDoc); } else { - collection.save(dbDoc, writeConcernToUse); + wr = collection.save(dbDoc, writeConcernToUse); } + handleAnyWriteResultErrors(wr, dbDoc, "save"); return dbDoc.get(ID); } }); @@ -880,7 +887,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Returns a {@link Query} for the given entity by its id. - * + * * @param object must not be {@literal null}. * @return */ @@ -1171,7 +1178,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Create the specified collection using the provided options - * + * * @param collectionName * @param collectionOptions * @return the collection that was created @@ -1193,7 +1200,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { * Map the results of an ad-hoc query on the default MongoDB collection to an object using the template's converter *

* The query document is specified as a standard DBObject and so is the fields specification. - * + * * @param collectionName name of the collection to retrieve the objects from * @param query the query document that specifies the criteria used to find a record * @param fields the document that specifies the fields to be returned @@ -1218,7 +1225,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { * The query document is specified as a standard DBObject and so is the fields specification. *

* Can be overridden by subclasses. - * + * * @param collectionName name of the collection to retrieve the objects from * @param query the query document that specifies the criteria used to find a record * @param fields the document that specifies the fields to be returned @@ -1248,7 +1255,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { * Map the results of an ad-hoc query on the default MongoDB collection to a List using the template's converter. *

* The query document is specified as a standard DBObject and so is the fields specification. - * + * * @param collectionName name of the collection to retrieve the objects from * @param query the query document that specifies the criteria used to find a record * @param fields the document that specifies the fields to be returned @@ -1287,7 +1294,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { * The first document that matches the query is returned and also removed from the collection in the database. *

* The query document is specified as a standard DBObject and so is the fields specification. - * + * * @param collectionName name of the collection to retrieve the objects from * @param query the query document that specifies the criteria used to find a record * @param entityClass the parameterized type of the returned list. @@ -1334,7 +1341,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Populates the id property of the saved object, if it's not set already. - * + * * @param savedObject * @param id */ @@ -1387,7 +1394,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { *

  • Execute the given {@link ConnectionCallback} for a {@link DBObject}.
  • *
  • Apply the given {@link DbObjectCallback} to each of the {@link DBObject}s to obtain the result.
  • *
      - * + * * @param * @param collectionCallback the callback to retrieve the {@link DBObject} with * @param objectCallback the {@link DbObjectCallback} to transform {@link DBObject}s into the actual domain type @@ -1510,9 +1517,16 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { String error = wr.getError(); if (error != null) { - - String message = String.format("Execution of %s%s failed: %s", operation, query == null ? "" : "' using '" - + query.toString() + "' query", error); + String message; + if (operation.equals("insert") || operation.equals("save")) { + // assuming the insert operations will begin with insert string + message = String.format("Insert/Save for %s failed: %s", query, error); + } else if (operation.equals("insert_list")) { + message = String.format("Insert list failed: %s", error); + } else { + message = String.format("Execution of %s%s failed: %s", operation, + query == null ? "" : "' using '" + query.toString() + "' query", error); + } if (WriteResultChecking.EXCEPTION == this.writeResultChecking) { throw new DataIntegrityViolationException(message); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java index dad91857f..293529c12 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 the original author or authors. + * Copyright 2011-2012 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. @@ -39,6 +39,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.converter.Converter; import org.springframework.dao.DataAccessException; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.mongodb.InvalidMongoDbApiUsageException; @@ -71,6 +72,7 @@ import com.mongodb.WriteResult; * * @author Oliver Gierke * @author Thomas Risberg + * @author Amol Nayak */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:infrastructure.xml") @@ -160,6 +162,112 @@ public class MongoTemplateTests { mongoTemplate.updateFirst(q, u, Person.class); } + /** + * @see DATAMONGO-480 + */ + @Test + public void throwsExceptionForDuplicateIds() { + + MongoTemplate template = new MongoTemplate(factory); + template.setWriteResultChecking(WriteResultChecking.EXCEPTION); + + Person person = new Person(new ObjectId(), "Amol"); + person.setAge(28); + + template.insert(person); + + try { + template.insert(person); + fail("Expected DataIntegrityViolationException!"); + } catch (DataIntegrityViolationException e) { + assertThat(e.getMessage(), containsString("E11000 duplicate key error index: database.person.$_id_ dup key:")); + } + } + + /** + * @see DATAMONGO-480 + */ + @Test + public void throwsExceptionForUpdateWithInvalidPushOperator() { + + MongoTemplate template = new MongoTemplate(factory); + template.setWriteResultChecking(WriteResultChecking.EXCEPTION); + + ObjectId id = new ObjectId(); + Person person = new Person(id, "Amol"); + person.setAge(28); + + template.insert(person); + + try { + + Query query = new Query(Criteria.where("firstName").is("Amol")); + Update upd = new Update().push("age", 29); + template.updateFirst(query, upd, Person.class); + fail("Expected DataIntegrityViolationException!"); + + } catch (DataIntegrityViolationException e) { + + assertThat(e.getMessage(), + is("Execution of update with '{ \"$push\" : { \"age\" : 29}}'' using '{ \"firstName\" : \"Amol\"}' " + + "query failed: Cannot apply $push/$pushAll modifier to non-array")); + } + } + + /** + * @see DATAMONGO-480 + */ + @Test + public void throwsExceptionForIndexViolationIfConfigured() { + + MongoTemplate template = new MongoTemplate(factory); + template.setWriteResultChecking(WriteResultChecking.EXCEPTION); + template.indexOps(Person.class).ensureIndex(new Index().on("firstName", Order.DESCENDING).unique()); + + Person person = new Person(new ObjectId(), "Amol"); + person.setAge(28); + + template.save(person); + + person = new Person(new ObjectId(), "Amol"); + person.setAge(28); + + try { + template.save(person); + fail("Expected DataIntegrityViolationException!"); + } catch (DataIntegrityViolationException e) { + assertThat(e.getMessage(), + containsString("E11000 duplicate key error index: database.person.$firstName_-1 dup key:")); + } + } + + /** + * @see DATAMONGO-480 + */ + @Test + public void rejectsDuplicateIdInInsertAll() { + + MongoTemplate template = new MongoTemplate(factory); + template.setWriteResultChecking(WriteResultChecking.EXCEPTION); + + ObjectId id = new ObjectId(); + Person person = new Person(id, "Amol"); + person.setAge(28); + + List records = new ArrayList(); + records.add(person); + records.add(person); + + try { + template.insertAll(records); + fail("Expected DataIntegrityViolationException!"); + } catch (DataIntegrityViolationException e) { + assertThat( + e.getMessage(), + startsWith("Insert list failed: E11000 duplicate key error index: database.person.$_id_ dup key: { : ObjectId")); + } + } + @Test public void testEnsureIndex() throws Exception { @@ -190,7 +298,7 @@ public class MongoTemplateTests { assertThat(dropDupes, is(true)); List indexInfoList = template.indexOps(Person.class).getIndexInfo(); - System.out.println(indexInfoList); + assertThat(indexInfoList.size(), is(2)); IndexInfo ii = indexInfoList.get(1); assertThat(ii.isUnique(), is(true)); @@ -932,7 +1040,7 @@ public class MongoTemplateTests { DBRef first = new DBRef(factory.getDb(), "foo", new ObjectId()); DBRef second = new DBRef(factory.getDb(), "bar", new ObjectId()); - template.updateFirst(null, Update.update("dbRefs", Arrays.asList(first, second)), ClassWithDBRefs.class); + template.updateFirst(null, update("dbRefs", Arrays.asList(first, second)), ClassWithDBRefs.class); } class ClassWithDBRefs { @@ -1095,7 +1203,7 @@ public class MongoTemplateTests { template.save(second); Query query = query(where("field").not().regex("Matthews")); - System.out.println(query.getQueryObject()); + List result = template.find(query, Sample.class); assertThat(result.size(), is(1)); assertThat(result.get(0).field, is("Beauford"));