Compare commits

...

19 Commits

Author SHA1 Message Date
Mark Paluch
88e60070d6 DATAMONGO-2543 - Release version 3.0.1 (Neumann SR1). 2020-06-10 14:02:26 +02:00
Mark Paluch
6c7039580f DATAMONGO-2543 - Prepare 3.0.1 (Neumann SR1). 2020-06-10 14:01:58 +02:00
Mark Paluch
fba003f215 DATAMONGO-2543 - Updated changelog. 2020-06-10 14:01:47 +02:00
Mark Paluch
1a3239554c DATAMONGO-2533 - Updated changelog. 2020-06-10 12:30:00 +02:00
Mark Paluch
37b541931d DATAMONGO-2532 - Updated changelog. 2020-06-10 11:48:56 +02:00
Mark Paluch
9038280f68 DATAMONGO-2565 - Polishing.
Add unit test to verify behavior. Cleanup code.

Original pull request: #869.
2020-06-10 10:15:12 +02:00
BraveLeeLee
75935a2bdb DATAMONGO-2565 - Evaluate correct expression when obtaining collation from MongoPersistentEntity.
Original pull request: #869.
2020-06-10 10:14:34 +02:00
Mark Paluch
d9ca3d7eb3 DATAMONGO-2562 - Polishing.
Fix typo in exception message.
2020-06-09 11:18:33 +02:00
Mark Paluch
04e77ad5ab DATAMONGO-2562 - Fix return type detection for suspended Kotlin methods.
See DATACMNS-1738 for further reference.
2020-06-09 11:18:33 +02:00
Mark Paluch
0ab39a17a7 DATAMONGO-2560 - Upgrade MongoDB drivers to 4.0.4. 2020-06-08 15:59:02 +02:00
Mark Paluch
49a6f13797 DATAMONGO-2542 - Polishing.
Fix nullable annotation.

Original pull request: #863.
2020-05-26 10:32:39 +02:00
Christoph Strobl
b0fd6f691b DATAMONGO-2542 - Shortcut PersistentPropertyPath resolution during query mapping.
By shortcutting the path resolution we avoid checking keywords like $in against a potential path expression.

Original pull request: #863.
2020-05-26 10:32:39 +02:00
Mark Paluch
b5778772d9 DATAMONGO-2545 - Polishing.
Fix warnings and typos.

Original pull request: #864.
2020-05-26 10:14:27 +02:00
Christoph Strobl
0f55fb305d DATAMONGO-2545 - Fix full Query Document binding resulting from SpEL.
We reenabled annotated queries using a SpEL expression resulting in the actual query document.

Original pull request: #864.
2020-05-26 10:14:27 +02:00
Christoph Strobl
5ae7547465 DATAMONGO-2545 - Fix regression in String query SpEL parameter binding.
We reenabled parameter binding within SpEL using query parameter placeholders ?0, ?1,... instead of their array index [0],[1],...

Original pull request: #864.
2020-05-26 10:14:27 +02:00
Christoph Strobl
cf4e04a30e DATAMONGO-2547 - Use target class ClassLoader instead of default CL when creating proxy instances.
Original pull request: #865.
2020-05-26 08:56:09 +02:00
Mark Paluch
89c1dc77d9 DATAMONGO-2553 - Fix reference documentation links.
Remove links to removed documentations sections.
2020-05-19 11:36:44 +02:00
Mark Paluch
a2c842b59b DATAMONGO-2534 - After release cleanups. 2020-05-12 12:56:12 +02:00
Mark Paluch
0cd0be9478 DATAMONGO-2534 - Prepare next development iteration. 2020-05-12 12:40:53 +02:00
20 changed files with 315 additions and 68 deletions

10
Jenkinsfile vendored
View File

@@ -3,7 +3,7 @@ pipeline {
triggers {
pollSCM 'H/10 * * * *'
upstream(upstreamProjects: "spring-data-commons/master", threshold: hudson.model.Result.SUCCESS)
upstream(upstreamProjects: "spring-data-commons/2.3.x", threshold: hudson.model.Result.SUCCESS)
}
options {
@@ -68,7 +68,7 @@ pipeline {
stage("test: baseline (jdk8)") {
when {
anyOf {
branch 'master'
branch '3.0.x'
not { triggeredBy 'UpstreamCause' }
}
}
@@ -94,7 +94,7 @@ pipeline {
stage("Test other configurations") {
when {
anyOf {
branch 'master'
branch '3.0.x'
not { triggeredBy 'UpstreamCause' }
}
}
@@ -164,7 +164,7 @@ pipeline {
stage('Release to artifactory') {
when {
anyOf {
branch 'master'
branch '3.0.x'
not { triggeredBy 'UpstreamCause' }
}
}
@@ -196,7 +196,7 @@ pipeline {
stage('Publish documentation') {
when {
branch 'master'
branch '3.0.x'
}
agent {
docker {

View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.0.0.RELEASE</version>
<version>3.0.1.RELEASE</version>
<packaging>pom</packaging>
<name>Spring Data MongoDB</name>
@@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>2.3.0.RELEASE</version>
<version>2.3.1.RELEASE</version>
</parent>
<modules>
@@ -26,8 +26,8 @@
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>2.3.0.RELEASE</springdata.commons>
<mongo>4.0.3</mongo>
<springdata.commons>2.3.1.RELEASE</springdata.commons>
<mongo>4.0.4</mongo>
<mongo.reactivestreams>${mongo}</mongo.reactivestreams>
<jmh.version>1.19</jmh.version>
</properties>

View File

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

View File

@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.0.0.RELEASE</version>
<version>3.0.1.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.0.0.RELEASE</version>
<version>3.0.1.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -254,7 +254,7 @@ public abstract class MongoDatabaseFactorySupport<C> implements MongoDatabaseFac
factory.addAdvice(new SessionAwareMethodInterceptor<>(session, target, ClientSession.class, MongoDatabase.class,
this::proxyDatabase, MongoCollection.class, this::proxyCollection));
return targetType.cast(factory.getProxy());
return targetType.cast(factory.getProxy(target.getClass().getClassLoader()));
}
}
}

View File

@@ -266,7 +266,7 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
factory.addAdvice(new SessionAwareMethodInterceptor<>(session, target, ClientSession.class, MongoDatabase.class,
this::proxyDatabase, MongoCollection.class, this::proxyCollection));
return targetType.cast(factory.getProxy());
return targetType.cast(factory.getProxy(target.getClass().getClassLoader()));
}
}
}

View File

@@ -202,7 +202,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
proxyFactory.addInterface(propertyType);
proxyFactory.addAdvice(interceptor);
return handler.populateId(property, dbref, proxyFactory.getProxy());
return handler.populateId(property, dbref, proxyFactory.getProxy(LazyLoadingProxy.class.getClassLoader()));
}
/**

View File

@@ -965,7 +965,7 @@ public class QueryMapper {
this.entity = entity;
this.mappingContext = context;
this.path = getPath(removePlaceholders(POSITIONAL_PARAMETER_PATTERN, name));
this.path = getPath(removePlaceholders(POSITIONAL_PARAMETER_PATTERN, name), property);
this.property = path == null ? property : path.getLeafProperty();
this.association = findAssociation();
}
@@ -1079,11 +1079,17 @@ public class QueryMapper {
* @return
*/
@Nullable
private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpression) {
private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpression,
@Nullable MongoPersistentProperty sourceProperty) {
String rawPath = removePlaceholders(POSITIONAL_OPERATOR,
removePlaceholders(DOT_POSITIONAL_PATTERN, pathExpression));
if (sourceProperty != null && sourceProperty.getOwner().equals(entity)) {
return mappingContext
.getPersistentPropertyPath(PropertyPath.from(sourceProperty.getName(), entity.getTypeInformation()));
}
PropertyPath path = forName(rawPath);
if (path == null || isPathToJavaLangClassProperty(path)) {
return null;

View File

@@ -164,7 +164,7 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
@Override
public org.springframework.data.mongodb.core.query.Collation getCollation() {
Object collationValue = collationExpression != null ? expression.getValue(getEvaluationContext(null), String.class)
Object collationValue = collationExpression != null ? collationExpression.getValue(getEvaluationContext(null), String.class)
: this.collation;
if (collationValue == null) {

View File

@@ -61,7 +61,7 @@ class QueryUtils {
return combinedSort;
});
return (Query) factory.getProxy();
return (Query) factory.getProxy(query.getClass().getClassLoader());
}
/**

View File

@@ -34,6 +34,7 @@ import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.util.ReactiveWrapperConverters;
import org.springframework.data.repository.util.ReactiveWrappers;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.ClassUtils;
@@ -50,6 +51,7 @@ public class ReactiveMongoQueryMethod extends MongoQueryMethod {
private static final ClassTypeInformation<Slice> SLICE_TYPE = ClassTypeInformation.from(Slice.class);
private final Method method;
private final Lazy<Boolean> isCollectionQuery;
/**
* Creates a new {@link ReactiveMongoQueryMethod} from the given {@link Method}.
@@ -87,11 +89,13 @@ public class ReactiveMongoQueryMethod extends MongoQueryMethod {
if (hasParameterOfType(method, Sort.class)) {
throw new IllegalStateException(String.format("Method must not have Pageable *and* Sort parameter. "
+ "Use sorting capabilities on Pageble instead! Offending method: %s", method.toString()));
+ "Use sorting capabilities on Pageable instead! Offending method: %s", method.toString()));
}
}
this.method = method;
this.isCollectionQuery = Lazy.of(() -> !(isPageQuery() || isSliceQuery())
&& ReactiveWrappers.isMultiValueType(metadata.getReturnType(method).getType()));
}
/*
@@ -109,7 +113,7 @@ public class ReactiveMongoQueryMethod extends MongoQueryMethod {
*/
@Override
public boolean isCollectionQuery() {
return !(isPageQuery() || isSliceQuery()) && ReactiveWrappers.isMultiValueType(method.getReturnType());
return isCollectionQuery.get();
}
/*

View File

@@ -38,6 +38,7 @@ import org.bson.Document;
import org.bson.Transformer;
import org.bson.codecs.*;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.json.JsonParseException;
import org.springframework.data.spel.EvaluationContextProvider;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable;
@@ -175,6 +176,7 @@ public class ParameterBindingDocumentCodec implements CollectibleCodec<Document>
return this.decode(reader, DecoderContext.builder().build());
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public Document decode(final BsonReader reader, final DecoderContext decoderContext) {
@@ -190,9 +192,27 @@ public class ParameterBindingDocumentCodec implements CollectibleCodec<Document>
Document document = new Document();
reader.readStartDocument();
while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {
String fieldName = reader.readName();
document.put(fieldName, readValue(reader, decoderContext));
try {
while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {
String fieldName = reader.readName();
Object value = readValue(reader, decoderContext);
document.put(fieldName, value);
}
} catch (JsonParseException e) {
try {
Object value = readValue(reader, decoderContext);
if (value instanceof Map<?, ?>) {
if (!((Map) value).isEmpty()) {
return new Document((Map<String, Object>) value);
}
}
} catch (Exception ex) {
e.addSuppressed(ex);
throw e;
}
}
reader.readEndDocument();

View File

@@ -25,6 +25,7 @@ import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.function.Supplier;
import java.util.regex.Matcher;
@@ -370,26 +371,33 @@ public class ParameterBindingJsonReader extends AbstractBsonReader {
if (token.getType().equals(JsonTokenType.UNQUOTED_STRING)) {
if (matcher.find()) {
int index = computeParameterIndex(matcher.group());
bindableValue.setValue(getBindableValueForIndex(index));
bindableValue.setType(bsonTypeForValue(getBindableValueForIndex(index)));
return bindableValue;
}
Matcher regexMatcher = EXPRESSION_BINDING_PATTERN.matcher(tokenValue);
if (regexMatcher.find()) {
String binding = regexMatcher.group();
String expression = binding.substring(3, binding.length() - 1);
Matcher inSpelMatcher = PARAMETER_BINDING_PATTERN.matcher(expression);
while (inSpelMatcher.find()) {
int index = computeParameterIndex(inSpelMatcher.group());
expression = expression.replace(inSpelMatcher.group(), getBindableValueForIndex(index).toString());
}
Object value = evaluateExpression(expression);
bindableValue.setValue(value);
bindableValue.setType(bsonTypeForValue(value));
return bindableValue;
}
if (matcher.find()) {
int index = computeParameterIndex(matcher.group());
bindableValue.setValue(getBindableValueForIndex(index));
bindableValue.setType(bsonTypeForValue(getBindableValueForIndex(index)));
return bindableValue;
}
bindableValue.setValue(tokenValue);
bindableValue.setType(BsonType.STRING);
return bindableValue;
@@ -398,28 +406,37 @@ public class ParameterBindingJsonReader extends AbstractBsonReader {
String computedValue = tokenValue;
boolean matched = false;
Matcher regexMatcher = EXPRESSION_BINDING_PATTERN.matcher(computedValue);
while (regexMatcher.find()) {
String binding = regexMatcher.group();
String expression = binding.substring(3, binding.length() - 1);
Matcher inSpelMatcher = PARAMETER_BINDING_PATTERN.matcher(expression);
while (inSpelMatcher.find()) {
int index = computeParameterIndex(inSpelMatcher.group());
expression = expression.replace(inSpelMatcher.group(), getBindableValueForIndex(index).toString());
}
computedValue = computedValue.replace(binding, nullSafeToString(evaluateExpression(expression)));
bindableValue.setValue(computedValue);
bindableValue.setType(BsonType.STRING);
return bindableValue;
}
while (matcher.find()) {
matched = true;
String group = matcher.group();
int index = computeParameterIndex(group);
computedValue = computedValue.replace(group, nullSafeToString(getBindableValueForIndex(index)));
}
if (!matched) {
Matcher regexMatcher = EXPRESSION_BINDING_PATTERN.matcher(tokenValue);
while (regexMatcher.find()) {
String binding = regexMatcher.group();
String expression = binding.substring(3, binding.length() - 1);
computedValue = computedValue.replace(binding, nullSafeToString(evaluateExpression(expression)));
}
}
bindableValue.setValue(computedValue);
bindableValue.setType(BsonType.STRING);
@@ -479,6 +496,9 @@ public class ParameterBindingJsonReader extends AbstractBsonReader {
if (ClassUtils.isAssignable(Iterable.class, type)) {
return BsonType.ARRAY;
}
if (ClassUtils.isAssignable(Map.class, type)) {
return BsonType.DOCUMENT;
}
return BsonType.UNDEFINED;
}

View File

@@ -22,7 +22,7 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -34,6 +34,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AliasFor;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.spel.ExtensionAwareEvaluationContextProvider;
import org.springframework.data.spel.spi.EvaluationContextExtension;
import org.springframework.data.util.ClassTypeInformation;
@@ -43,6 +44,7 @@ import org.springframework.data.util.ClassTypeInformation;
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
*/
@ExtendWith(MockitoExtension.class)
public class BasicMongoPersistentEntityUnitTests {
@@ -53,7 +55,7 @@ public class BasicMongoPersistentEntityUnitTests {
@Test
void subclassInheritsAtDocumentAnnotation() {
BasicMongoPersistentEntity<Person> entity = new BasicMongoPersistentEntity<Person>(
BasicMongoPersistentEntity<Person> entity = new BasicMongoPersistentEntity<>(
ClassTypeInformation.from(Person.class));
assertThat(entity.getCollection()).isEqualTo("contacts");
}
@@ -61,7 +63,7 @@ public class BasicMongoPersistentEntityUnitTests {
@Test
void evaluatesSpELExpression() {
MongoPersistentEntity<Company> entity = new BasicMongoPersistentEntity<Company>(
MongoPersistentEntity<Company> entity = new BasicMongoPersistentEntity<>(
ClassTypeInformation.from(Company.class));
assertThat(entity.getCollection()).isEqualTo("35");
}
@@ -74,7 +76,7 @@ public class BasicMongoPersistentEntityUnitTests {
when(context.getBean("myBean")).thenReturn(provider);
BasicMongoPersistentEntity<DynamicallyMapped> entity = new BasicMongoPersistentEntity<DynamicallyMapped>(
BasicMongoPersistentEntity<DynamicallyMapped> entity = new BasicMongoPersistentEntity<>(
ClassTypeInformation.from(DynamicallyMapped.class));
entity.setEvaluationContextProvider(new ExtensionAwareEvaluationContextProvider(context));
@@ -87,7 +89,7 @@ public class BasicMongoPersistentEntityUnitTests {
@Test // DATAMONGO-937
void shouldDetectLanguageCorrectly() {
BasicMongoPersistentEntity<DocumentWithLanguage> entity = new BasicMongoPersistentEntity<DocumentWithLanguage>(
BasicMongoPersistentEntity<DocumentWithLanguage> entity = new BasicMongoPersistentEntity<>(
ClassTypeInformation.from(DocumentWithLanguage.class));
assertThat(entity.getLanguage()).isEqualTo("spanish");
@@ -99,11 +101,11 @@ public class BasicMongoPersistentEntityUnitTests {
doReturn(true).when(propertyMock).isExplicitLanguageProperty();
doReturn(Number.class).when(propertyMock).getActualType();
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<>(
ClassTypeInformation.from(AnyDocument.class));
entity.addPersistentProperty(propertyMock);
assertThatExceptionOfType(MappingException.class).isThrownBy(() -> entity.verify());
assertThatExceptionOfType(MappingException.class).isThrownBy(entity::verify);
}
@Test // DATAMONGO-1053
@@ -112,7 +114,7 @@ public class BasicMongoPersistentEntityUnitTests {
doReturn(true).when(propertyMock).isExplicitLanguageProperty();
doReturn(String.class).when(propertyMock).getActualType();
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<>(
ClassTypeInformation.from(AnyDocument.class));
entity.addPersistentProperty(propertyMock);
@@ -125,7 +127,7 @@ public class BasicMongoPersistentEntityUnitTests {
@Test // DATAMONGO-1053
void verifyShouldIgnoreNonExplicitLanguageProperty() {
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<>(
ClassTypeInformation.from(AnyDocument.class));
when(propertyMock.isExplicitLanguageProperty()).thenReturn(false);
entity.addPersistentProperty(propertyMock);
@@ -147,11 +149,11 @@ public class BasicMongoPersistentEntityUnitTests {
doReturn(dbRefMock).when(propertyMock).getDBRef();
doReturn(true).when(dbRefMock).lazy();
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<>(
ClassTypeInformation.from(AnyDocument.class));
entity.addPersistentProperty(propertyMock);
assertThatExceptionOfType(MappingException.class).isThrownBy(() -> entity.verify());
assertThatExceptionOfType(MappingException.class).isThrownBy(entity::verify);
}
@Test // DATAMONGO-1157
@@ -165,11 +167,11 @@ public class BasicMongoPersistentEntityUnitTests {
doReturn(dbRefMock).when(propertyMock).getDBRef();
doReturn(true).when(dbRefMock).lazy();
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<>(
ClassTypeInformation.from(AnyDocument.class));
entity.addPersistentProperty(propertyMock);
assertThatExceptionOfType(MappingException.class).isThrownBy(() -> entity.verify());
assertThatExceptionOfType(MappingException.class).isThrownBy(entity::verify);
}
@Test // DATAMONGO-1157
@@ -183,7 +185,7 @@ public class BasicMongoPersistentEntityUnitTests {
doReturn(dbRefMock).when(propertyMock).getDBRef();
doReturn(true).when(dbRefMock).lazy();
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<>(
ClassTypeInformation.from(AnyDocument.class));
entity.addPersistentProperty(propertyMock);
entity.verify();
@@ -201,7 +203,7 @@ public class BasicMongoPersistentEntityUnitTests {
doReturn(dbRefMock).when(propertyMock).getDBRef();
doReturn(false).when(dbRefMock).lazy();
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<>(
ClassTypeInformation.from(AnyDocument.class));
entity.addPersistentProperty(propertyMock);
entity.verify();
@@ -212,7 +214,7 @@ public class BasicMongoPersistentEntityUnitTests {
@Test // DATAMONGO-1291
void metaInformationShouldBeReadCorrectlyFromInheritedDocumentAnnotation() {
BasicMongoPersistentEntity<DocumentWithCustomAnnotation> entity = new BasicMongoPersistentEntity<DocumentWithCustomAnnotation>(
BasicMongoPersistentEntity<DocumentWithCustomAnnotation> entity = new BasicMongoPersistentEntity<>(
ClassTypeInformation.from(DocumentWithCustomAnnotation.class));
assertThat(entity.getCollection()).isEqualTo("collection-1");
@@ -221,7 +223,7 @@ public class BasicMongoPersistentEntityUnitTests {
@Test // DATAMONGO-1373
void metaInformationShouldBeReadCorrectlyFromComposedDocumentAnnotation() {
BasicMongoPersistentEntity<DocumentWithComposedAnnotation> entity = new BasicMongoPersistentEntity<DocumentWithComposedAnnotation>(
BasicMongoPersistentEntity<DocumentWithComposedAnnotation> entity = new BasicMongoPersistentEntity<>(
ClassTypeInformation.from(DocumentWithComposedAnnotation.class));
assertThat(entity.getCollection()).isEqualTo("custom-collection");
@@ -233,7 +235,7 @@ public class BasicMongoPersistentEntityUnitTests {
BasicMongoPersistentEntity<MappedWithExtension> entity = new BasicMongoPersistentEntity<>(
ClassTypeInformation.from(MappedWithExtension.class));
entity.setEvaluationContextProvider(
new ExtensionAwareEvaluationContextProvider(Arrays.asList(new SampleExtension())));
new ExtensionAwareEvaluationContextProvider(Collections.singletonList(new SampleExtension())));
assertThat(entity.getCollection()).isEqualTo("collectionName");
}
@@ -256,6 +258,18 @@ public class BasicMongoPersistentEntityUnitTests {
assertThat(entity.getCollation()).isEqualTo(org.springframework.data.mongodb.core.query.Collation.of("en_US"));
}
@Test // DATAMONGO-2565
void usesCorrectExpressionsForCollectionAndCollation() {
BasicMongoPersistentEntity<WithCollectionAndCollationFromSpEL> entity = new BasicMongoPersistentEntity<>(
ClassTypeInformation.from(WithCollectionAndCollationFromSpEL.class));
entity.setEvaluationContextProvider(
new ExtensionAwareEvaluationContextProvider(Collections.singletonList(new SampleExtension())));
assertThat(entity.getCollection()).isEqualTo("collectionName");
assertThat(entity.getCollation()).isEqualTo(Collation.of("en_US"));
}
@Test // DATAMONGO-2341
void detectsShardedEntityCorrectly() {
@@ -340,6 +354,9 @@ public class BasicMongoPersistentEntityUnitTests {
@Document(collation = "#{myCollation}")
class WithCollationFromSpEL {}
@Document(collection = "#{myProperty}", collation = "#{myCollation}")
class WithCollectionAndCollationFromSpEL {}
@Document(collation = "en_US")
class WithSimpleCollation {}

View File

@@ -17,6 +17,9 @@ package org.springframework.data.mongodb.util.json;
import static org.assertj.core.api.Assertions.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
@@ -26,6 +29,7 @@ import java.util.List;
import org.bson.Document;
import org.bson.codecs.DecoderContext;
import org.junit.jupiter.api.Test;
import org.springframework.data.spel.EvaluationContextProvider;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.standard.SpelExpressionParser;
@@ -264,10 +268,92 @@ class ParameterBindingJsonReaderUnitTests {
assertThat(target).isEqualTo(Document.parse("{\"$and\": [{\"v1\": {\"$in\": [1]}}]}"));
}
@Test // DATAMONGO-2545
void shouldABindArgumentsViaIndexInSpelExpressions() {
Object[] args = new Object[] { "yess", "nooo" };
StandardEvaluationContext evaluationContext = (StandardEvaluationContext) EvaluationContextProvider.DEFAULT
.getEvaluationContext(args);
ParameterBindingJsonReader reader = new ParameterBindingJsonReader(
"{ 'isBatman' : ?#{ T(" + this.getClass().getName() + ").isBatman() ? [0] : [1] }}",
new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext));
Document target = new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build());
assertThat(target).isEqualTo(new Document("isBatman", "nooo"));
}
@Test // DATAMONGO-2545
void shouldAllowMethodArgumentPlaceholdersInSpelExpressions/*becuase this worked before*/() {
Object[] args = new Object[] { "yess", "nooo" };
StandardEvaluationContext evaluationContext = (StandardEvaluationContext) EvaluationContextProvider.DEFAULT
.getEvaluationContext(args);
ParameterBindingJsonReader reader = new ParameterBindingJsonReader(
"{ 'isBatman' : ?#{ T(" + this.getClass().getName() + ").isBatman() ? '?0' : '?1' }}",
new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext));
Document target = new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build());
assertThat(target).isEqualTo(new Document("isBatman", "nooo"));
}
@Test // DATAMONGO-2545
void shouldAllowMethodArgumentPlaceholdersInQuotedSpelExpressions/*because this worked before*/() {
Object[] args = new Object[] { "yess", "nooo" };
StandardEvaluationContext evaluationContext = (StandardEvaluationContext) EvaluationContextProvider.DEFAULT
.getEvaluationContext(args);
ParameterBindingJsonReader reader = new ParameterBindingJsonReader(
"{ 'isBatman' : \"?#{ T(" + this.getClass().getName() + ").isBatman() ? '?0' : '?1' }\" }",
new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext));
Document target = new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build());
assertThat(target).isEqualTo(new Document("isBatman", "nooo"));
}
@Test // DATAMONGO-2545
void evaluatesSpelExpressionDefiningEntireQuery() {
Object[] args = new Object[] {};
StandardEvaluationContext evaluationContext = (StandardEvaluationContext) EvaluationContextProvider.DEFAULT
.getEvaluationContext(args);
evaluationContext.setRootObject(new DummySecurityObject(new DummyWithId("wonderwoman")));
String json = "?#{ T(" + this.getClass().getName()
+ ").isBatman() ? {'_class': { '$eq' : 'region' }} : { '$and' : { {'_class': { '$eq' : 'region' } }, {'user.supervisor': principal.id } } } }";
ParameterBindingJsonReader reader = new ParameterBindingJsonReader(json,
new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext));
Document target = new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build());
assertThat(target)
.isEqualTo(new Document("$and", Arrays.asList(new Document("_class", new Document("$eq", "region")),
new Document("user.supervisor", "wonderwoman"))));
}
private static Document parse(String json, Object... args) {
ParameterBindingJsonReader reader = new ParameterBindingJsonReader(json, args);
return new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build());
}
// DATAMONGO-2545
public static boolean isBatman() {
return false;
}
@Data
@AllArgsConstructor
public static class DummySecurityObject {
DummyWithId principal;
}
@Data
@AllArgsConstructor
public static class DummyWithId {
String id;
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.repository.query
import kotlinx.coroutines.flow.Flow
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.data.mongodb.core.mapping.MongoMappingContext
import org.springframework.data.mongodb.repository.Person
import org.springframework.data.projection.SpelAwareProxyProjectionFactory
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata
import org.springframework.data.repository.kotlin.CoroutineCrudRepository
import kotlin.coroutines.Continuation
/**
* Unit tests for [ReactiveMongoQueryMethod] using Coroutine repositories.
*
* @author Mark Paluch
*/
class ReactiveMongoQueryMethodCoroutineUnitTests {
val projectionFactory = SpelAwareProxyProjectionFactory()
interface PersonRepository : CoroutineCrudRepository<Person, String> {
suspend fun findSuspendAllByName(): Flow<Person>
fun findAllByName(): Flow<Person>
}
@Test // DATAMONGO-2562
internal fun `should consider methods returning Flow as collection queries`() {
val method = PersonRepository::class.java.getMethod("findAllByName")
val queryMethod = ReactiveMongoQueryMethod(method, DefaultRepositoryMetadata(PersonRepository::class.java), projectionFactory, MongoMappingContext())
assertThat(queryMethod.isCollectionQuery).isTrue()
}
@Test // DATAMONGO-2562
internal fun `should consider suspended methods returning Flow as collection queries`() {
val method = PersonRepository::class.java.getMethod("findSuspendAllByName", Continuation::class.java)
val queryMethod = ReactiveMongoQueryMethod(method, DefaultRepositoryMetadata(PersonRepository::class.java), projectionFactory, MongoMappingContext())
assertThat(queryMethod.isCollectionQuery).isTrue()
}
}

View File

@@ -87,7 +87,7 @@
* Out-of-the-box support for `java.util.Currency` in object mapping.
* Support for the bulk operations introduced in MongoDB 2.6.
* Upgrade to Querydsl 4.
* Assert compatibility with MongoDB 3.0 and MongoDB Java Driver 3.2 (see: <<mongo.mongo-3>>).
* Assert compatibility with MongoDB 3.0 and MongoDB Java Driver 3.2.
[[new-features.1-8-0]]
== What's New in Spring Data MongoDB 1.8
@@ -101,7 +101,7 @@
[[new-features.1-7-0]]
== What's New in Spring Data MongoDB 1.7
* Assert compatibility with MongoDB 3.0 and MongoDB Java Driver 3-beta3 (see: <<mongo.mongo-3>>).
* Assert compatibility with MongoDB 3.0 and MongoDB Java Driver 3-beta3.
* Support JSR-310 and ThreeTen back-port date/time types.
* Allow `Stream` as a query method return type (see: <<mongodb.repositories.queries>>).
* https://geojson.org/[GeoJSON] support in both domain types and queries (see: <<mongo.geo-json>>).

View File

@@ -1,6 +1,36 @@
Spring Data MongoDB Changelog
=============================
Changes in version 3.0.1.RELEASE (2020-06-10)
---------------------------------------------
* DATAMONGO-2565 - MongoPersistentEntity.getCollation(...) evaluates document expression instead of collation expression.
* DATAMONGO-2562 - CoroutineCrudRepository findAllBy throwing IncorrectResultSizeDataAccessException when more than 1 result.
* DATAMONGO-2560 - Upgrade MongoDB drivers to 4.0.4.
* DATAMONGO-2553 - Reference documentation contains links to removed sections.
* DATAMONGO-2547 - java.lang.IllegalArgumentException: com.mongodb.client.MongoDatabase referenced from a method is not visible from class loader.
* DATAMONGO-2545 - Backwards incompatibilities on @Query.
* DATAMONGO-2543 - Release 3.0.1 (Neumann SR1).
* DATAMONGO-2542 - Don't attempt to map query operator keywords to properties.
Changes in version 2.2.8.RELEASE (2020-06-10)
---------------------------------------------
* DATAMONGO-2565 - MongoPersistentEntity.getCollation(...) evaluates document expression instead of collation expression.
* DATAMONGO-2547 - java.lang.IllegalArgumentException: com.mongodb.client.MongoDatabase referenced from a method is not visible from class loader.
* DATAMONGO-2545 - Backwards incompatibilities on @Query.
* DATAMONGO-2542 - Don't attempt to map query operator keywords to properties.
* DATAMONGO-2535 - Remove Travis CI.
* DATAMONGO-2533 - Release 2.2.8 (Moore SR8).
* DATAMONGO-2509 - Polish documentation for 3.0 release.
Changes in version 2.1.18.RELEASE (2020-06-10)
----------------------------------------------
* DATAMONGO-2547 - java.lang.IllegalArgumentException: com.mongodb.client.MongoDatabase referenced from a method is not visible from class loader.
* DATAMONGO-2535 - Remove Travis CI.
* DATAMONGO-2532 - Release 2.1.18 (Lovelace SR18).
Changes in version 3.0.0.RELEASE (2020-05-12)
---------------------------------------------
* DATAMONGO-2541 - Upgrade to MongoDB 4.0.3 Drivers.
@@ -3029,3 +3059,5 @@ Repository

View File

@@ -1,4 +1,4 @@
Spring Data MongoDB 3.0 GA
Spring Data MongoDB 3.0.1
Copyright (c) [2010-2019] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").
@@ -15,3 +15,4 @@ conditions of the subcomponent's license, as noted in the LICENSE file.