diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ExpressionEvaluatingParameterBinder.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ExpressionEvaluatingParameterBinder.java index 21aa12cec..b8fd95c1e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ExpressionEvaluatingParameterBinder.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ExpressionEvaluatingParameterBinder.java @@ -19,11 +19,14 @@ import lombok.EqualsAndHashCode; import lombok.Value; import lombok.experimental.UtilityClass; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -221,9 +224,33 @@ class ExpressionEvaluatingParameterBinder { return base64representation; } + if (value instanceof UUID) { + + UUID uuid = (UUID) value; + + if (binding.isQuoted()) { + return uuid.toString(); + } + + String base64representation = DatatypeConverter.printBase64Binary(asBinary(uuid)); + return "{ '$binary' : '" + base64representation + "', '$type' : '" + BSON.B_UUID + "'}"; + } + return JSON.serialize(value); } + private static byte[] asBinary(UUID uuid) { + + byte[] bytes = new byte[16]; + + ByteBuffer bb = ByteBuffer.wrap(bytes) // + .order(ByteOrder.LITTLE_ENDIAN) // + .putLong(uuid.getMostSignificantBits()) // + .putLong(uuid.getLeastSignificantBits()); + + return bb.array(); + } + /** * Evaluates the given {@code expressionString}. * diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java index e33219fd3..141d5f17b 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java @@ -27,6 +27,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -1094,6 +1095,17 @@ public abstract class AbstractPersonRepositoryIntegrationTests { assertThat(users.get(0), is(dave)); } + @Test // DATAMONGO-1911 + public void findByUUIDShouldReturnCorrectResult() { + + dave.setUniqueId(UUID.randomUUID()); + repository.save(dave); + + Person dave = repository.findByUniqueId(this.dave.getUniqueId()); + + assertThat(dave, is(equalTo(dave))); + } + @Test // DATAMONGO-1245 public void findByExampleShouldResolveStuffCorrectly() { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java index 4c165f739..ccf4489b5 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Set; +import java.util.UUID; import org.springframework.data.geo.Point; import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; @@ -34,6 +35,7 @@ import org.springframework.data.mongodb.core.mapping.Field; * @author Oliver Gierke * @author Thomas Darimont * @author Christoph Strobl + * @author Mark Paluch */ @Document public class Person extends Contact { @@ -56,6 +58,8 @@ public class Person extends Contact { private @Field("add") Address address; private Set
shippingAddresses; + private UUID uniqueId; + @DBRef User creator; @DBRef(lazy = true) User coworker; @@ -196,6 +200,14 @@ public class Person extends Contact { this.shippingAddresses = addresses; } + public UUID getUniqueId() { + return uniqueId; + } + + public void setUniqueId(UUID uniqueId) { + this.uniqueId = uniqueId; + } + /* (non-Javadoc) * @see org.springframework.data.mongodb.repository.Contact#getName() */ diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java index ad355258b..601abc09b 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java @@ -19,6 +19,7 @@ import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Optional; +import java.util.UUID; import java.util.stream.Stream; import org.springframework.data.domain.Page; @@ -318,6 +319,10 @@ public interface PersonRepository extends MongoRepository, Query @Query("{ firstname : :#{#firstname}}") List findWithSpelByFirstnameForSpELExpressionWithParameterVariableOnly(@Param("firstname") String firstname); + // DATAMONGO-1911 + @Query("{ uniqueId: ?0}") + Person findByUniqueId(UUID uniqueId); + /** * Returns the count of {@link Person} with the given firstname. Uses {@link CountQuery} annotation to define the * query to be executed. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java index a08c95c6d..d7cf599fc 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.UUID; import javax.xml.bind.DatatypeConverter; @@ -321,6 +322,34 @@ public class StringBasedMongoQueryUnitTests { assertThat(query.getQueryObject().toJson(), is(reference.getQueryObject().toJson())); } + @Test // DATAMONGO-1911 + public void shouldSupportNonQuotedUUIDReplacement() { + + UUID uuid = UUID.fromString("864de43b-e3ea-f1e4-3663-fb8240b659b9"); + ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, (Object) uuid); + StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAsUUID", UUID.class); + + org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); + org.springframework.data.mongodb.core.query.Query reference = new BasicQuery( + "{'lastname' : { $binary:\"5PHq4zvkTYa5WbZAgvtjNg==\", $type: \"03\"}}"); + + assertThat(query.getQueryObject().toJson(), is(reference.getQueryObject().toJson())); + } + + @Test // DATAMONGO-1911 + public void shouldSupportQuotedUUIDReplacement() { + + UUID uuid = UUID.randomUUID(); + ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, (Object) uuid); + StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAsStringUUID", UUID.class); + + org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); + org.springframework.data.mongodb.core.query.Query reference = new BasicQuery( + "{'lastname' : '" + uuid.toString() + "'}"); + + assertThat(query.getQueryObject().toJson(), is(reference.getQueryObject().toJson())); + } + @Test // DATAMONGO-1454 public void shouldSupportExistsProjection() { @@ -551,6 +580,12 @@ public class StringBasedMongoQueryUnitTests { @Query("{ 'lastname' : ?0 }") Person findByLastnameAsBinary(byte[] lastname); + @Query("{ 'lastname' : ?0 }") + Person findByLastnameAsUUID(UUID lastname); + + @Query("{ 'lastname' : '?0' }") + Person findByLastnameAsStringUUID(UUID lastname); + @Query("{ 'lastname' : '?0' }") Person findByLastnameQuoted(String lastname);