Browse Source

DATAMONGO-480 - Consider WriteResult for insert(…) and save(…) methods.

1.0.x
Amol Nayak 14 years ago committed by Oliver Gierke
parent
commit
2e6a9a6ee7
  1. 48
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
  2. 116
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java

48
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 Graeme Rocher
* @author Mark Pollack * @author Mark Pollack
* @author Oliver Gierke * @author Oliver Gierke
* @author Amol Nayak
*/ */
public class MongoTemplate implements MongoOperations, ApplicationContextAware { public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@ -734,11 +735,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT, collectionName, MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT, collectionName,
entityClass, dbDoc, null); entityClass, dbDoc, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction); WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
WriteResult wr;
if (writeConcernToUse == null) { if (writeConcernToUse == null) {
collection.insert(dbDoc); wr = collection.insert(dbDoc);
} else { } else {
collection.insert(dbDoc, writeConcernToUse); wr = collection.insert(dbDoc, writeConcernToUse);
} }
handleAnyWriteResultErrors(wr, dbDoc, "insert");
return dbDoc.get(ID); return dbDoc.get(ID);
} }
}); });
@ -757,11 +760,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT_LIST, collectionName, null, MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT_LIST, collectionName, null,
null, null); null, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction); WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
WriteResult wr;
if (writeConcernToUse == null) { if (writeConcernToUse == null) {
collection.insert(dbDocList); wr = collection.insert(dbDocList);
} else { } 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; return null;
} }
}); });
@ -788,11 +793,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.SAVE, collectionName, entityClass, MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.SAVE, collectionName, entityClass,
dbDoc, null); dbDoc, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction); WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
WriteResult wr;
if (writeConcernToUse == null) { if (writeConcernToUse == null) {
collection.save(dbDoc); wr = collection.save(dbDoc);
} else { } else {
collection.save(dbDoc, writeConcernToUse); wr = collection.save(dbDoc, writeConcernToUse);
} }
handleAnyWriteResultErrors(wr, dbDoc, "save");
return dbDoc.get(ID); 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. * Returns a {@link Query} for the given entity by its id.
* *
* @param object must not be {@literal null}. * @param object must not be {@literal null}.
* @return * @return
*/ */
@ -1171,7 +1178,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/** /**
* Create the specified collection using the provided options * Create the specified collection using the provided options
* *
* @param collectionName * @param collectionName
* @param collectionOptions * @param collectionOptions
* @return the collection that was created * @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 * Map the results of an ad-hoc query on the default MongoDB collection to an object using the template's converter
* <p/> * <p/>
* The query document is specified as a standard DBObject and so is the fields specification. * 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 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 query the query document that specifies the criteria used to find a record
* @param fields the document that specifies the fields to be returned * @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. * The query document is specified as a standard DBObject and so is the fields specification.
* <p/> * <p/>
* Can be overridden by subclasses. * Can be overridden by subclasses.
* *
* @param collectionName name of the collection to retrieve the objects from * @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 query the query document that specifies the criteria used to find a record
* @param fields the document that specifies the fields to be returned * @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. * Map the results of an ad-hoc query on the default MongoDB collection to a List using the template's converter.
* <p/> * <p/>
* The query document is specified as a standard DBObject and so is the fields specification. * 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 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 query the query document that specifies the criteria used to find a record
* @param fields the document that specifies the fields to be returned * @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 first document that matches the query is returned and also removed from the collection in the database.
* <p/> * <p/>
* The query document is specified as a standard DBObject and so is the fields specification. * 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 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 query the query document that specifies the criteria used to find a record
* @param entityClass the parameterized type of the returned list. * @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. * Populates the id property of the saved object, if it's not set already.
* *
* @param savedObject * @param savedObject
* @param id * @param id
*/ */
@ -1387,7 +1394,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* <li>Execute the given {@link ConnectionCallback} for a {@link DBObject}.</li> * <li>Execute the given {@link ConnectionCallback} for a {@link DBObject}.</li>
* <li>Apply the given {@link DbObjectCallback} to each of the {@link DBObject}s to obtain the result.</li> * <li>Apply the given {@link DbObjectCallback} to each of the {@link DBObject}s to obtain the result.</li>
* <ol> * <ol>
* *
* @param <T> * @param <T>
* @param collectionCallback the callback to retrieve the {@link DBObject} with * @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 * @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(); String error = wr.getError();
if (error != null) { if (error != null) {
String message;
String message = String.format("Execution of %s%s failed: %s", operation, query == null ? "" : "' using '" if (operation.equals("insert") || operation.equals("save")) {
+ query.toString() + "' query", error); // 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) { if (WriteResultChecking.EXCEPTION == this.writeResultChecking) {
throw new DataIntegrityViolationException(message); throw new DataIntegrityViolationException(message);

116
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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.beans.factory.annotation.Autowired;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException; import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
@ -71,6 +72,7 @@ import com.mongodb.WriteResult;
* *
* @author Oliver Gierke * @author Oliver Gierke
* @author Thomas Risberg * @author Thomas Risberg
* @author Amol Nayak
*/ */
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:infrastructure.xml") @ContextConfiguration("classpath:infrastructure.xml")
@ -160,6 +162,112 @@ public class MongoTemplateTests {
mongoTemplate.updateFirst(q, u, Person.class); 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<Person> records = new ArrayList<Person>();
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 @Test
public void testEnsureIndex() throws Exception { public void testEnsureIndex() throws Exception {
@ -190,7 +298,7 @@ public class MongoTemplateTests {
assertThat(dropDupes, is(true)); assertThat(dropDupes, is(true));
List<IndexInfo> indexInfoList = template.indexOps(Person.class).getIndexInfo(); List<IndexInfo> indexInfoList = template.indexOps(Person.class).getIndexInfo();
System.out.println(indexInfoList);
assertThat(indexInfoList.size(), is(2)); assertThat(indexInfoList.size(), is(2));
IndexInfo ii = indexInfoList.get(1); IndexInfo ii = indexInfoList.get(1);
assertThat(ii.isUnique(), is(true)); assertThat(ii.isUnique(), is(true));
@ -932,7 +1040,7 @@ public class MongoTemplateTests {
DBRef first = new DBRef(factory.getDb(), "foo", new ObjectId()); DBRef first = new DBRef(factory.getDb(), "foo", new ObjectId());
DBRef second = new DBRef(factory.getDb(), "bar", 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 { class ClassWithDBRefs {
@ -1095,7 +1203,7 @@ public class MongoTemplateTests {
template.save(second); template.save(second);
Query query = query(where("field").not().regex("Matthews")); Query query = query(where("field").not().regex("Matthews"));
System.out.println(query.getQueryObject());
List<Sample> result = template.find(query, Sample.class); List<Sample> result = template.find(query, Sample.class);
assertThat(result.size(), is(1)); assertThat(result.size(), is(1));
assertThat(result.get(0).field, is("Beauford")); assertThat(result.get(0).field, is("Beauford"));

Loading…
Cancel
Save