From d7ae95a7791a03f4f1170d4f89c9ec885f55deb8 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 15 Aug 2012 15:16:44 +0200 Subject: [PATCH] DATAMONGO-505 - Fixed handling of parameter binding of associations and collection values. Instead of converting given association values as-is, ConvertingParameterAccessor now converts each collection value into a DBRef individually. --- .../query/ConvertingParameterAccessor.java | 57 ++++++++++++++- .../ConvertingParameterAccessorUnitTests.java | 72 ++++++++++++++++++- 2 files changed, 126 insertions(+), 3 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java index 8d9c6b1b0..98a06f329 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java @@ -15,7 +15,11 @@ */ package org.springframework.data.mongodb.repository.query; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.Iterator; +import java.util.List; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -25,6 +29,9 @@ import org.springframework.data.mongodb.core.geo.Point; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; + +import com.mongodb.DBRef; /** * Custom {@link ParameterAccessor} that uses a {@link MongoWriter} to serialize parameters into Mongo format. @@ -158,7 +165,28 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor { * @see org.springframework.data.mongodb.repository.ConvertingParameterAccessor.PotentiallConvertingIterator#nextConverted() */ public Object nextConverted(MongoPersistentProperty property) { - return property.isAssociation() ? writer.toDBRef(next(), property) : getConvertedValue(next()); + + Object next = next(); + + if (next == null) { + return null; + } + + if (property.isAssociation()) { + if (next.getClass().isArray() || next instanceof Iterable) { + + List dbRefs = new ArrayList(); + for (Object element : asCollection(next)) { + dbRefs.add(writer.toDBRef(element, property)); + } + + return dbRefs; + } else { + return writer.toDBRef(next, property); + } + } + + return getConvertedValue(next); } /* @@ -170,6 +198,33 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor { } } + /** + * Returns the given object as {@link Collection}. Will do a copy of it if it implements {@link Iterable} or is an + * array. Will return an empty {@link Collection} in case {@literal null} is given. Will wrap all other types into a + * single-element collction + * + * @param source + * @return + */ + private static Collection asCollection(Object source) { + + if (source instanceof Iterable) { + + List result = new ArrayList(); + for (Object element : (Iterable) source) { + result.add(element); + } + + return result; + } + + if (source == null) { + return Collections.emptySet(); + } + + return source.getClass().isArray() ? CollectionUtils.arrayToList(source) : Collections.singleton(source); + } + /** * Custom {@link Iterator} that adds a method to access elements in a converted manner. * diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessorUnitTests.java index 6b70a23c6..10f40c2d2 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessorUnitTests.java @@ -15,11 +15,13 @@ */ package org.springframework.data.mongodb.repository.query; -import static org.mockito.Mockito.*; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; -import static org.hamcrest.CoreMatchers.*; +import static org.mockito.Mockito.*; import java.util.Arrays; +import java.util.Collection; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -27,7 +29,11 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; +import org.springframework.data.mongodb.core.mapping.DBRef; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; +import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; +import org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor.PotentiallyConvertingIterator; import com.mongodb.BasicDBList; @@ -76,4 +82,66 @@ public class ConvertingParameterAccessorUnitTests { assertThat(result, is((Object) reference)); } + + /** + * @see DATAMONGO-505 + */ + @Test + public void convertsAssociationsToDBRef() { + + Property property = new Property(); + property.id = 5L; + + Object result = setupAndConvert(property); + + assertThat(result, is(instanceOf(com.mongodb.DBRef.class))); + com.mongodb.DBRef dbRef = (com.mongodb.DBRef) result; + assertThat(dbRef.getRef(), is("property")); + assertThat(dbRef.getId(), is((Object) 5L)); + } + + /** + * @see DATAMONGO-505 + */ + @Test + public void convertsAssociationsToDBRefForCollections() { + + Property property = new Property(); + property.id = 5L; + + Object result = setupAndConvert(Arrays.asList(property)); + + assertThat(result, is(instanceOf(Collection.class))); + Collection collection = (Collection) result; + + assertThat(collection, hasSize(1)); + Object element = collection.iterator().next(); + + assertThat(element, is(instanceOf(com.mongodb.DBRef.class))); + com.mongodb.DBRef dbRef = (com.mongodb.DBRef) element; + assertThat(dbRef.getRef(), is("property")); + assertThat(dbRef.getId(), is((Object) 5L)); + } + + private Object setupAndConvert(Object... parameters) { + + MongoParameterAccessor delegate = new StubParameterAccessor(parameters); + PotentiallyConvertingIterator iterator = new ConvertingParameterAccessor(converter, delegate).iterator(); + + MongoPersistentEntity entity = context.getPersistentEntity(Entity.class); + MongoPersistentProperty property = entity.getPersistentProperty("property"); + + return iterator.nextConverted(property); + } + + static class Entity { + + @DBRef + Property property; + } + + static class Property { + + Long id; + } }