DATAMONGO-1911 - Fix UUID serialization in String-based queries.
We now render to the correct UUID representation in String-based queries. Unquoted values render to $binary representation, quoted UUIDs are rendered with their toString() value. Previously we used JSON.serialize() to encode values to JSON. The com.mongodb.util.JSON serializer does not produce JSON that is compatible with Document.parse. It uses an older JSON format that preceded the MongoDB Extended JSON specification. Original Pull Request: #544
This commit is contained in:
committed by
Christoph Strobl
parent
56b6748068
commit
51d5c52193
@@ -19,11 +19,14 @@ import lombok.EqualsAndHashCode;
|
|||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@@ -221,9 +224,33 @@ class ExpressionEvaluatingParameterBinder {
|
|||||||
return base64representation;
|
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);
|
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}.
|
* Evaluates the given {@code expressionString}.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import java.util.Arrays;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@@ -1094,6 +1095,17 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
|
|||||||
assertThat(users.get(0), is(dave));
|
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
|
@Test // DATAMONGO-1245
|
||||||
public void findByExampleShouldResolveStuffCorrectly() {
|
public void findByExampleShouldResolveStuffCorrectly() {
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.springframework.data.geo.Point;
|
import org.springframework.data.geo.Point;
|
||||||
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
|
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
|
||||||
@@ -34,6 +35,7 @@ import org.springframework.data.mongodb.core.mapping.Field;
|
|||||||
* @author Oliver Gierke
|
* @author Oliver Gierke
|
||||||
* @author Thomas Darimont
|
* @author Thomas Darimont
|
||||||
* @author Christoph Strobl
|
* @author Christoph Strobl
|
||||||
|
* @author Mark Paluch
|
||||||
*/
|
*/
|
||||||
@Document
|
@Document
|
||||||
public class Person extends Contact {
|
public class Person extends Contact {
|
||||||
@@ -56,6 +58,8 @@ public class Person extends Contact {
|
|||||||
private @Field("add") Address address;
|
private @Field("add") Address address;
|
||||||
private Set<Address> shippingAddresses;
|
private Set<Address> shippingAddresses;
|
||||||
|
|
||||||
|
private UUID uniqueId;
|
||||||
|
|
||||||
@DBRef User creator;
|
@DBRef User creator;
|
||||||
|
|
||||||
@DBRef(lazy = true) User coworker;
|
@DBRef(lazy = true) User coworker;
|
||||||
@@ -196,6 +200,14 @@ public class Person extends Contact {
|
|||||||
this.shippingAddresses = addresses;
|
this.shippingAddresses = addresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UUID getUniqueId() {
|
||||||
|
return uniqueId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUniqueId(UUID uniqueId) {
|
||||||
|
this.uniqueId = uniqueId;
|
||||||
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see org.springframework.data.mongodb.repository.Contact#getName()
|
* @see org.springframework.data.mongodb.repository.Contact#getName()
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import java.util.Collection;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
@@ -318,6 +319,10 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
|
|||||||
@Query("{ firstname : :#{#firstname}}")
|
@Query("{ firstname : :#{#firstname}}")
|
||||||
List<Person> findWithSpelByFirstnameForSpELExpressionWithParameterVariableOnly(@Param("firstname") String firstname);
|
List<Person> 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
|
* Returns the count of {@link Person} with the given firstname. Uses {@link CountQuery} annotation to define the
|
||||||
* query to be executed.
|
* query to be executed.
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import javax.xml.bind.DatatypeConverter;
|
import javax.xml.bind.DatatypeConverter;
|
||||||
|
|
||||||
@@ -321,6 +322,34 @@ public class StringBasedMongoQueryUnitTests {
|
|||||||
assertThat(query.getQueryObject().toJson(), is(reference.getQueryObject().toJson()));
|
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
|
@Test // DATAMONGO-1454
|
||||||
public void shouldSupportExistsProjection() {
|
public void shouldSupportExistsProjection() {
|
||||||
|
|
||||||
@@ -551,6 +580,12 @@ public class StringBasedMongoQueryUnitTests {
|
|||||||
@Query("{ 'lastname' : ?0 }")
|
@Query("{ 'lastname' : ?0 }")
|
||||||
Person findByLastnameAsBinary(byte[] lastname);
|
Person findByLastnameAsBinary(byte[] lastname);
|
||||||
|
|
||||||
|
@Query("{ 'lastname' : ?0 }")
|
||||||
|
Person findByLastnameAsUUID(UUID lastname);
|
||||||
|
|
||||||
|
@Query("{ 'lastname' : '?0' }")
|
||||||
|
Person findByLastnameAsStringUUID(UUID lastname);
|
||||||
|
|
||||||
@Query("{ 'lastname' : '?0' }")
|
@Query("{ 'lastname' : '?0' }")
|
||||||
Person findByLastnameQuoted(String lastname);
|
Person findByLastnameQuoted(String lastname);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user