Compare commits

...

7 Commits

Author SHA1 Message Date
Spring Buildmaster
6323a86560 DATAMONGO-559 - Release version 1.1.1.RELEASE. 2012-10-17 14:59:59 -07:00
Oliver Gierke
3b0b7315e1 DATAMONGO-559 - Prepare 1.1.1.RELEASE release. 2012-10-17 17:53:39 -04:00
Oliver Gierke
4aeba6f92d DATAMONGO-553 - MappingMongoConverter now uses MongoPersistentProperty.usePropertyAccess() to decide whether to use property access.
Introduced usePropertyAccess() method on MongoPersistentProperty and added an implementation that forces that into being true for Throwable.cause as using the field would cause a cycle as it points to this in the first place but rather massages the value in the getter.
2012-10-17 17:27:15 -04:00
Oliver Gierke
342f9ae837 DATAMONGO-551 - MongoTemplate can now persist plain Strings if they are valid JSON.
Added some code to MongoTemplate that inspects the object to be saved for being a String. If it is we try to parse the given String into a JSON document and continue as if we had been given a DBObject initially. Non-parseable Strings are rejected with a MappingException.
2012-10-15 11:49:29 -04:00
Oliver Gierke
66d98b355e DATAMONGO-550 - Fixed potential NullPointerExceptions in MongoTemplate.
Guarded access to results of mappingContext.getPersistentEntity(…) to prevent NullPointerExceptions in case the template is used with non-entity types like a plain DBObject. Added some custom logic for plain BasicDBObjects to get the _id field populated appropriately.
2012-10-15 11:38:00 -04:00
Oliver Gierke
cabbe747f8 DATAMONGO-549 - Fixed potential NullPointerException in MongoTemplate.
Added assertions and guards against MongoPersistentEntity lookups resulting in null values.
2012-10-11 08:49:49 +02:00
Spring Buildmaster
5ff3064acd DATAMONGO-541 - Prepare next development iteration. 2012-10-10 05:30:21 -07:00
14 changed files with 199 additions and 20 deletions

View File

@@ -6,7 +6,7 @@
<name>Spring Data MongoDB Distribution</name>
<description>Spring Data project for MongoDB</description>
<url>http://www.springsource.org/spring-data/mongodb</url>
<version>1.1.0.RELEASE</version>
<version>1.1.1.RELEASE</version>
<packaging>pom</packaging>
<modules>
<module>spring-data-mongodb</module>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.1.0.RELEASE</version>
<version>1.1.1.RELEASE</version>
<relativePath>../spring-data-mongodb-parent/pom.xml</relativePath>
</parent>
<artifactId>spring-data-mongodb-cross-store</artifactId>
@@ -42,7 +42,7 @@
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>1.1.0.RELEASE</version>
<version>1.1.1.RELEASE</version>
</dependency>
<dependency>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.1.0.RELEASE</version>
<version>1.1.1.RELEASE</version>
<relativePath>../spring-data-mongodb-parent/pom.xml</relativePath>
</parent>
<artifactId>spring-data-mongodb-log4j</artifactId>

View File

@@ -6,7 +6,7 @@
<name>Spring Data MongoDB Parent</name>
<description>Spring Data project for MongoDB</description>
<url>http://www.springsource.org/spring-data/mongodb</url>
<version>1.1.0.RELEASE</version>
<version>1.1.1.RELEASE</version>
<packaging>pom</packaging>
<developers>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.1.0.RELEASE</version>
<version>1.1.1.RELEASE</version>
<relativePath>../spring-data-mongodb-parent/pom.xml</relativePath>
</parent>
<artifactId>spring-data-mongodb</artifactId>

View File

@@ -101,6 +101,7 @@ import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
import com.mongodb.WriteResult;
import com.mongodb.util.JSON;
import com.mongodb.util.JSONParseException;
/**
* Primary implementation of {@link MongoOperations}.
@@ -505,13 +506,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
public <T> T findById(Object id, Class<T> entityClass) {
MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(entityClass);
return findById(id, entityClass, persistentEntity.getCollection());
return findById(id, entityClass, determineCollectionName(entityClass));
}
public <T> T findById(Object id, Class<T> entityClass, String collectionName) {
MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(entityClass);
MongoPersistentProperty idProperty = persistentEntity.getIdProperty();
MongoPersistentProperty idProperty = persistentEntity == null ? null : persistentEntity.getIdProperty();
String idKey = idProperty == null ? ID : idProperty.getName();
return doFindOne(collectionName, new BasicDBObject(idKey, id), null, entityClass);
}
@@ -733,15 +733,20 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
public void save(Object objectToSave) {
Assert.notNull(objectToSave);
save(objectToSave, determineEntityCollectionName(objectToSave));
}
public void save(Object objectToSave, String collectionName) {
Assert.notNull(objectToSave);
Assert.hasText(collectionName);
MongoPersistentEntity<?> mongoPersistentEntity = getPersistentEntity(objectToSave.getClass());
// No optimistic locking -> simple save
if (!mongoPersistentEntity.hasVersionProperty()) {
if (mongoPersistentEntity == null || !mongoPersistentEntity.hasVersionProperty()) {
doSave(collectionName, objectToSave, this.mongoConverter);
return;
}
@@ -786,14 +791,24 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
}
@SuppressWarnings("unchecked")
protected <T> void doSave(String collectionName, T objectToSave, MongoWriter<T> writer) {
assertUpdateableIdIfNotSet(objectToSave);
BasicDBObject dbDoc = new BasicDBObject();
DBObject dbDoc = new BasicDBObject();
maybeEmitEvent(new BeforeConvertEvent<T>(objectToSave));
writer.write(objectToSave, dbDoc);
if (!(objectToSave instanceof String)) {
writer.write(objectToSave, dbDoc);
} else {
try {
objectToSave = (T) JSON.parse((String) objectToSave);
} catch (JSONParseException e) {
throw new MappingException("Could not parse given String to save into a JSON document!", e);
}
}
maybeEmitEvent(new BeforeSaveEvent<T>(objectToSave, dbDoc));
Object id = saveDBObject(collectionName, dbDoc, objectToSave.getClass());
@@ -977,7 +992,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
Assert.notNull(object);
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(object.getClass());
MongoPersistentProperty idProp = entity.getIdProperty();
MongoPersistentProperty idProp = entity == null ? null : entity.getIdProperty();
if (idProp == null) {
throw new MappingException("No id property found for object of type " + entity.getType().getName());
@@ -993,7 +1008,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
private void assertUpdateableIdIfNotSet(Object entity) {
MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(entity.getClass());
MongoPersistentProperty idProperty = persistentEntity.getIdProperty();
MongoPersistentProperty idProperty = persistentEntity == null ? null : persistentEntity.getIdProperty();
if (idProperty == null) {
return;
@@ -1433,6 +1448,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return;
}
if (savedObject instanceof BasicDBObject) {
DBObject dbObject = (DBObject) savedObject;
dbObject.put(ID, id);
return;
}
MongoPersistentProperty idProp = getIdPropertyFor(savedObject.getClass());
if (idProp == null) {
@@ -1550,7 +1571,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
private MongoPersistentProperty getIdPropertyFor(Class<?> type) {
return mappingContext.getPersistentEntity(type).getIdProperty();
MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(type);
return persistentEntity == null ? null : persistentEntity.getIdProperty();
}
private <T> String determineEntityCollectionName(T obj) {

View File

@@ -346,13 +346,14 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
}
final BeanWrapper<MongoPersistentEntity<Object>, Object> wrapper = BeanWrapper.create(obj, conversionService);
// Write the ID
final MongoPersistentProperty idProperty = entity.getIdProperty();
if (!dbo.containsField("_id") && null != idProperty) {
boolean fieldAccessOnly = idProperty.usePropertyAccess() ? false : useFieldAccessOnly;
try {
Object id = wrapper.getProperty(idProperty, Object.class, useFieldAccessOnly);
Object id = wrapper.getProperty(idProperty, Object.class, fieldAccessOnly);
dbo.put("_id", idMapper.convertId(id));
} catch (ConversionException ignored) {
}
@@ -366,7 +367,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return;
}
Object propertyObj = wrapper.getProperty(prop, prop.getType(), useFieldAccessOnly);
boolean fieldAccessOnly = prop.usePropertyAccess() ? false : useFieldAccessOnly;
Object propertyObj = wrapper.getProperty(prop, prop.getType(), fieldAccessOnly);
if (null != propertyObj) {
if (!conversions.isSimpleType(propertyObj.getClass())) {

View File

@@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import com.mongodb.DBObject;
@@ -46,6 +47,8 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
private static final Set<Class<?>> SUPPORTED_ID_TYPES = new HashSet<Class<?>>();
private static final Set<String> SUPPORTED_ID_PROPERTY_NAMES = new HashSet<String>();
private static final Field CAUSE_FIELD;
static {
SUPPORTED_ID_TYPES.add(ObjectId.class);
SUPPORTED_ID_TYPES.add(String.class);
@@ -53,6 +56,8 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
SUPPORTED_ID_PROPERTY_NAMES.add("id");
SUPPORTED_ID_PROPERTY_NAMES.add("_id");
CAUSE_FIELD = ReflectionUtils.findField(Throwable.class, "cause");
}
/**
@@ -155,4 +160,12 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
public boolean isVersionProperty() {
return getField().isAnnotationPresent(Version.class);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.MongoPersistentProperty#usePropertyAccess()
*/
public boolean usePropertyAccess() {
return CAUSE_FIELD.equals(getField());
}
}

View File

@@ -80,4 +80,12 @@ public interface MongoPersistentProperty extends PersistentProperty<MongoPersist
return source.getFieldName();
}
}
/**
* Returns whether property access shall be used for reading the property value. This means it will use the getter
* instead of field access.
*
* @return
*/
boolean usePropertyAccess();
}

View File

@@ -123,6 +123,13 @@ public class SimpleMongoMappingContext extends
return false;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.MongoPersistentProperty#usePropertyAccess()
*/
public boolean usePropertyAccess() {
return false;
}
}
static class SimpleMongoPersistentEntity<T> extends BasicPersistentEntity<T, MongoPersistentProperty> implements

View File

@@ -24,8 +24,10 @@ import static org.springframework.data.mongodb.core.query.Update.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.bson.types.ObjectId;
import org.joda.time.DateTime;
@@ -40,9 +42,11 @@ 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.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.convert.CustomConversions;
@@ -60,6 +64,7 @@ import org.springframework.data.mongodb.core.query.Update;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
@@ -140,6 +145,7 @@ public class MongoTemplateTests {
template.dropCollection(TestClass.class);
template.dropCollection(Sample.class);
template.dropCollection(MyPerson.class);
template.dropCollection("collection");
}
@Test
@@ -1322,6 +1328,87 @@ public class MongoTemplateTests {
assertThat(template.findAll(PersonWithConvertedId.class, collectionName).isEmpty(), is(true));
}
/**
* @see DATAMONGO-549
*/
public void savesMapCorrectly() {
Map<String, String> map = new HashMap<String, String>();
map.put("key", "value");
template.save(map, "maps");
}
/**
* @see DATAMONGO-549
*/
@Test(expected = MappingException.class)
public void savesMongoPrimitiveObjectCorrectly() {
template.save(new Object(), "collection");
}
/**
* @see DATAMONGO-549
*/
@Test(expected = IllegalArgumentException.class)
public void rejectsNullObjectToBeSaved() {
template.save(null);
}
/**
* @see DATAMONGO-550
*/
@Test
public void savesPlainDbObjectCorrectly() {
DBObject dbObject = new BasicDBObject("foo", "bar");
template.save(dbObject, "collection");
assertThat(dbObject.containsField("_id"), is(true));
}
/**
* @see DATAMONGO-550
*/
@Test(expected = InvalidDataAccessApiUsageException.class)
public void rejectsPlainObjectWithOutExplicitCollection() {
DBObject dbObject = new BasicDBObject("foo", "bar");
template.save(dbObject, "collection");
template.findById(dbObject.get("_id"), DBObject.class);
}
/**
* @see DATAMONGO-550
*/
@Test
public void readsPlainDbObjectById() {
DBObject dbObject = new BasicDBObject("foo", "bar");
template.save(dbObject, "collection");
DBObject result = template.findById(dbObject.get("_id"), DBObject.class, "collection");
assertThat(result.get("foo"), is(dbObject.get("foo")));
assertThat(result.get("_id"), is(dbObject.get("_id")));
}
/**
* @see DATAMONGO-551
*/
@Test
public void writesPlainString() {
template.save("{ 'foo' : 'bar' }", "collection");
}
/**
* @see DATAMONGO-551
*/
@Test(expected = MappingException.class)
public void rejectsNonJsonStringForSave() {
template.save("Foobar!", "collection");
}
static class MyId {
String first;

View File

@@ -918,7 +918,7 @@ public class MappingMongoConverterUnitTests {
Object contacts = result.get("contacts");
assertThat(contacts, is(instanceOf(Collection.class)));
assertThat(((Collection<?>) contacts).size(), is(2));
assertThat(((Collection<Object>) contacts), hasItem(nullValue()));
assertThat((Collection<Object>) contacts, hasItem(nullValue()));
}
/**
@@ -1310,6 +1310,19 @@ public class MappingMongoConverterUnitTests {
assertThat(type.toString(), is("_"));
}
/**
* @see DATAMONGO-533
*/
@Test
public void marshalsThrowableCorrectly() {
ThrowableWrapper wrapper = new ThrowableWrapper();
wrapper.throwable = new Exception();
DBObject dbObject = new BasicDBObject();
converter.write(wrapper, dbObject);
}
static class GenericType<T> {
T content;
}
@@ -1498,6 +1511,11 @@ public class MappingMongoConverterUnitTests {
String name;
}
static class ThrowableWrapper {
Throwable throwable;
}
private class LocalDateToDateConverter implements Converter<LocalDate, Date> {
public Date convert(LocalDate source) {

View File

@@ -68,6 +68,16 @@ public class BasicMongoPersistentPropertyUnitTests {
getPropertyFor(ReflectionUtils.findField(Person.class, "ssn"));
}
/**
* @see DATAMONGO-553
*/
@Test
public void usesPropertyAccessForThrowableCause() {
MongoPersistentProperty property = getPropertyFor(ReflectionUtils.findField(Throwable.class, "cause"));
assertThat(property.usePropertyAccess(), is(true));
}
private MongoPersistentProperty getPropertyFor(Field field) {
return new BasicMongoPersistentProperty(field, null, entity, new SimpleTypeHolder());
}

View File

@@ -1,6 +1,17 @@
Spring Data MongoDB Changelog
=============================================
Changes in version 1.1.1.GA (2012-10-17)
----------------------------------------
** Bug
* [DATAMONGO-549] - MongoTemplate.save(…) suffers from potential NullPointException
* [DATAMONGO-550] - MongoTemplate.save(BasicDBObject, String) results in NPE (after upgrading to 1.1.0.RELEASE
* [DATAMONGO-551] - MongoTemplate.save(String, String) results in NPE (after upgrading to 1.1.0.RELEASE
** Task
* [DATAMONGO-559] - Release 1.1.1.RELEASE
Changes in version 1.1.0.GA (2012-10-10)
----------------------------------------
** Bug