Compare commits

...

21 Commits
3.1.6 ... 3.1.8

Author SHA1 Message Date
Mark Paluch
12b4aab834 Release version 3.1.8 (2020.0.8).
See #3617
2021-04-14 11:33:25 +02:00
Mark Paluch
db06756c8f Prepare 3.1.8 (2020.0.8).
See #3617
2021-04-14 11:32:47 +02:00
Mark Paluch
b319b8a589 Updated changelog.
See #3617
2021-04-14 11:32:42 +02:00
Mark Paluch
a516795759 Updated changelog.
See #3597
2021-04-14 11:17:41 +02:00
Mark Paluch
bab08502a5 Polishing.
Fix nullability annotations for isEqual(…) parameters. Fix generics. Reformat code.

Add tests.

See #3414
Original pull request: #3615.
2021-04-13 09:40:00 +02:00
Clement Petit
3e1f95bc94 Handle nested Pattern and Document in Criteria.equals(…).
Closes #3414
Original pull request: #3615.
2021-04-13 09:39:59 +02:00
Mark Paluch
5c153dc76e Polishing.
Use ObjectUtils for empty check.

See #3623
Original pull request: #3625.
2021-04-13 09:09:42 +02:00
Christoph Strobl
8f4e207d97 Fix NPE in declarative aggregation execution.
This commit fixes an issue where using a simple return type leads to NPE when the actual aggregation result does not contain any values.

Closes: #3623
Original pull request: #3625.
2021-04-13 09:09:42 +02:00
Christoph Strobl
5000a40d72 Fix query mapping resolution of properties using underscore within field name.
Closes: #3601
Original pull request: #3607.
2021-04-09 12:27:08 +02:00
Mark Paluch
fb59f49dae After release cleanups.
See #3598
2021-03-31 18:29:41 +02:00
Mark Paluch
f3c1e014e9 Prepare next development iteration.
See #3598
2021-03-31 18:29:38 +02:00
Mark Paluch
f52cc3be1f Release version 3.1.7 (2020.0.7).
See #3598
2021-03-31 18:19:52 +02:00
Mark Paluch
1bda93858c Prepare 3.1.7 (2020.0.7).
See #3598
2021-03-31 18:19:19 +02:00
Mark Paluch
1808970daf Updated changelog.
See #3598
2021-03-31 18:19:13 +02:00
Mark Paluch
558fc28cce Updated changelog.
See #3595
2021-03-31 17:26:09 +02:00
Mark Paluch
16bef54f11 Use StringUtils.replace(…) instead of String.replaceAll(…) for mapKeyDotReplacement.
We now use StringUtils.replace(…) to replace the map key dot in MappingMongoConverter. StringUtils perform a plain search instead of using Regex which improves the overall performance.

Closes #3613
2021-03-30 14:29:50 +02:00
Mark Paluch
d68a812e1b Polishing.
Omit StreamUtils usage if input is a collection. Remove superfluous Flux.from(…). Simplify test and migrate test to JUnit 5.

See #3609.
Original pull request: #3611.
2021-03-29 11:02:34 +02:00
Clément Petit
ccb9f111d9 Return saved entity reference instead of original reference.
Make SimpleReactiveMongoRepository#saveAll(Publisher<S>) return the saved entity references instead of the original references.

Closes #3609
Original pull request: #3611.
2021-03-29 10:55:36 +02:00
Mark Paluch
f64b177c8f Updated changelog.
See #3558
2021-03-17 11:31:32 +01:00
Mark Paluch
c0c7ba767f After release cleanups.
See #3561
2021-03-17 11:02:15 +01:00
Mark Paluch
7639701f3f Prepare next development iteration.
See #3561
2021-03-17 11:02:13 +01:00
16 changed files with 415 additions and 82 deletions

View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.1.6</version>
<version>3.1.8</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.4.6</version>
<version>2.4.8</version>
</parent>
<modules>
@@ -26,7 +26,7 @@
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>2.4.6</springdata.commons>
<springdata.commons>2.4.8</springdata.commons>
<mongo>4.1.2</mongo>
<mongo.reactivestreams>${mongo}</mongo.reactivestreams>
<jmh.version>1.19</jmh.version>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.1.6</version>
<version>3.1.8</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.1.6</version>
<version>3.1.8</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.1.6</version>
<version>3.1.8</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -75,6 +75,7 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
@@ -182,6 +183,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* any translation but rather reject a {@link Map} with keys containing dots causing the conversion for the entire
* object to fail. If further customization of the translation is needed, have a look at
* {@link #potentiallyEscapeMapKey(String)} as well as {@link #potentiallyUnescapeMapKey(String)}.
* <p>
* {@code mapKeyDotReplacement} is used as-is during replacement operations without further processing (i.e. regex or
* normalization).
*
* @param mapKeyDotReplacement the mapKeyDotReplacement to set. Can be {@literal null}.
*/
@@ -900,7 +904,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
source));
}
return source.replaceAll("\\.", mapKeyDotReplacement);
return StringUtils.replace(source, ".", mapKeyDotReplacement);
}
/**
@@ -928,7 +932,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* @return
*/
protected String potentiallyUnescapeMapKey(String source) {
return mapKeyDotReplacement == null ? source : source.replaceAll(mapKeyDotReplacement, "\\.");
return mapKeyDotReplacement == null ? source : StringUtils.replace(source, mapKeyDotReplacement, ".");
}
/**

View File

@@ -1086,8 +1086,8 @@ public class QueryMapper {
removePlaceholders(DOT_POSITIONAL_PATTERN, pathExpression));
if (sourceProperty != null && sourceProperty.getOwner().equals(entity)) {
return mappingContext
.getPersistentPropertyPath(PropertyPath.from(sourceProperty.getName(), entity.getTypeInformation()));
return mappingContext.getPersistentPropertyPath(
PropertyPath.from(Pattern.quote(sourceProperty.getName()), entity.getTypeInformation()));
}
PropertyPath path = forName(rawPath);
@@ -1146,6 +1146,13 @@ public class QueryMapper {
return forName(path.substring(0, path.length() - 3) + "id");
}
// Ok give it another try quoting
try {
return PropertyPath.from(Pattern.quote(path), entity.getTypeInformation());
} catch (PropertyReferenceException | InvalidPersistentPropertyPath ex) {
}
return null;
}
}

View File

@@ -20,8 +20,10 @@ import static org.springframework.util.ObjectUtils.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -58,6 +60,7 @@ import com.mongodb.BasicDBList;
* @author Christoph Strobl
* @author Mark Paluch
* @author Andreas Zink
* @author Clément Petit
*/
public class Criteria implements CriteriaDefinition {
@@ -895,15 +898,15 @@ public class Criteria implements CriteriaDefinition {
* @param right
* @return
*/
private boolean isEqual(Object left, Object right) {
private boolean isEqual(@Nullable Object left, @Nullable Object right) {
if (left == null) {
return right == null;
}
if (Pattern.class.isInstance(left)) {
if (left instanceof Pattern) {
if (!Pattern.class.isInstance(right)) {
if (!(right instanceof Pattern)) {
return false;
}
@@ -914,6 +917,52 @@ public class Criteria implements CriteriaDefinition {
&& leftPattern.flags() == rightPattern.flags();
}
if (left instanceof Document) {
if (!(right instanceof Document)) {
return false;
}
Document leftDocument = (Document) left;
Document rightDocument = (Document) right;
Iterator<Entry<String, Object>> leftIterator = leftDocument.entrySet().iterator();
Iterator<Entry<String, Object>> rightIterator = rightDocument.entrySet().iterator();
while (leftIterator.hasNext() && rightIterator.hasNext()) {
Map.Entry<String, Object> leftEntry = leftIterator.next();
Map.Entry<String, Object> rightEntry = rightIterator.next();
if (!isEqual(leftEntry.getKey(), rightEntry.getKey())
|| !isEqual(leftEntry.getValue(), rightEntry.getValue())) {
return false;
}
}
return !leftIterator.hasNext() && !rightIterator.hasNext();
}
if (Collection.class.isAssignableFrom(left.getClass())) {
if (!Collection.class.isAssignableFrom(right.getClass())) {
return false;
}
Collection<?> leftCollection = (Collection<?>) left;
Collection<?> rightCollection = (Collection<?>) right;
Iterator<?> leftIterator = leftCollection.iterator();
Iterator<?> rightIterator = rightCollection.iterator();
while (leftIterator.hasNext() && rightIterator.hasNext()) {
if (!isEqual(leftIterator.next(), rightIterator.next())) {
return false;
}
}
return !leftIterator.hasNext() && !rightIterator.hasNext();
}
return ObjectUtils.nullSafeEquals(left, right);
}

View File

@@ -33,6 +33,7 @@ import org.springframework.data.repository.query.QueryMethodEvaluationContextPro
import org.springframework.expression.ExpressionParser;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
@@ -163,9 +164,9 @@ abstract class AggregationUtils {
* @throws IllegalArgumentException when none of the above rules is met.
*/
@Nullable
static <T> T extractSimpleTypeResult(Document source, Class<T> targetType, MongoConverter converter) {
static <T> T extractSimpleTypeResult(@Nullable Document source, Class<T> targetType, MongoConverter converter) {
if (source.isEmpty()) {
if (ObjectUtils.isEmpty(source)) {
return null;
}

View File

@@ -18,6 +18,7 @@ package org.springframework.data.mongodb.repository.support;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@@ -215,7 +216,7 @@ public class SimpleMongoRepository<T, ID> implements MongoRepository<T, ID> {
Assert.notNull(ids, "The given Ids of entities not be null!");
return findAll(new Query(new Criteria(entityInformation.getIdAttribute())
.in(Streamable.of(ids).stream().collect(StreamUtils.toUnmodifiableList()))));
.in(toCollection(ids))));
}
/*
@@ -266,10 +267,10 @@ public class SimpleMongoRepository<T, ID> implements MongoRepository<T, ID> {
Assert.notNull(entities, "The given Iterable of entities not be null!");
List<S> list = Streamable.of(entities).stream().collect(StreamUtils.toUnmodifiableList());
Collection<S> list = toCollection(entities);
if (list.isEmpty()) {
return list;
return Collections.emptyList();
}
return new ArrayList<>(mongoOperations.insertAll(list));
@@ -374,6 +375,11 @@ public class SimpleMongoRepository<T, ID> implements MongoRepository<T, ID> {
return where(entityInformation.getIdAttribute()).is(id);
}
private static <E> Collection<E> toCollection(Iterable<E> ids) {
return ids instanceof Collection ? (Collection<E>) ids
: StreamUtils.createStreamFromIterator(ids.iterator()).collect(Collectors.toList());
}
private List<T> findAll(@Nullable Query query) {
if (query == null) {

View File

@@ -21,10 +21,11 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.io.Serializable;
import java.util.List;
import java.util.Collection;
import java.util.stream.Collectors;
import org.reactivestreams.Publisher;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.domain.Example;
@@ -47,6 +48,7 @@ import com.mongodb.client.result.DeleteResult;
* @author Oliver Gierke
* @author Christoph Strobl
* @author Ruben J Garcia
* @author Clément Petit
* @since 2.0
*/
public class SimpleReactiveMongoRepository<T, ID extends Serializable> implements ReactiveMongoRepository<T, ID> {
@@ -173,7 +175,7 @@ public class SimpleReactiveMongoRepository<T, ID extends Serializable> implement
Assert.notNull(ids, "The given Iterable of Id's must not be null!");
return findAll(new Query(new Criteria(entityInformation.getIdAttribute())
.in(Streamable.of(ids).stream().collect(StreamUtils.toUnmodifiableList()))));
.in(toCollection(ids))));
}
/*
@@ -274,9 +276,9 @@ public class SimpleReactiveMongoRepository<T, ID extends Serializable> implement
Assert.notNull(entities, "The given Iterable of entities must not be null!");
List<S> source = Streamable.of(entities).stream().collect(StreamUtils.toUnmodifiableList());
Collection<S> source = toCollection(entities);
return source.isEmpty() ? Flux.empty() : Flux.from(mongoOperations.insertAll(source));
return source.isEmpty() ? Flux.empty() : mongoOperations.insertAll(source);
}
/*
@@ -333,8 +335,8 @@ public class SimpleReactiveMongoRepository<T, ID extends Serializable> implement
Assert.notNull(entityStream, "The given Publisher of entities must not be null!");
return Flux.from(entityStream).flatMap(entity -> entityInformation.isNew(entity) ? //
mongoOperations.insert(entity, entityInformation.getCollectionName()).then(Mono.just(entity)) : //
mongoOperations.save(entity, entityInformation.getCollectionName()).then(Mono.just(entity)));
mongoOperations.insert(entity, entityInformation.getCollectionName()) : //
mongoOperations.save(entity, entityInformation.getCollectionName()));
}
/*
@@ -436,8 +438,12 @@ public class SimpleReactiveMongoRepository<T, ID extends Serializable> implement
return where(entityInformation.getIdAttribute()).is(id);
}
private Flux<T> findAll(Query query) {
private static <E> Collection<E> toCollection(Iterable<E> ids) {
return ids instanceof Collection ? (Collection<E>) ids
: StreamUtils.createStreamFromIterator(ids.iterator()).collect(Collectors.toList());
}
private Flux<T> findAll(Query query) {
return mongoOperations.find(query, entityInformation.getJavaType(), entityInformation.getCollectionName());
}
}

View File

@@ -781,7 +781,8 @@ public class QueryMapperUnitTests {
Query query = query(byExample(probe).and("listOfItems").exists(true));
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(Foo.class));
assertThat(document).containsEntry("embedded\\._id", "conflux").containsEntry("my_items", new org.bson.Document("$exists", true));
assertThat(document).containsEntry("embedded\\._id", "conflux").containsEntry("my_items",
new org.bson.Document("$exists", true));
}
@Test // DATAMONGO-1988
@@ -1011,6 +1012,76 @@ public class QueryMapperUnitTests {
assertThat(target).isEqualTo(org.bson.Document.parse("{\"$text\" : { \"$search\" : \"test\" }}"));
}
@Test // GH-3601
void resolvesFieldnameWithUnderscoresCorrectly() {
Query query = query(where("fieldname_with_underscores").exists(true));
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(),
context.getPersistentEntity(WithPropertyUsingUnderscoreInName.class));
assertThat(document)
.isEqualTo(new org.bson.Document("fieldname_with_underscores", new org.bson.Document("$exists", true)));
}
@Test // GH-3601
void resolvesMappedFieldnameWithUnderscoresCorrectly() {
Query query = query(where("renamed_fieldname_with_underscores").exists(true));
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(),
context.getPersistentEntity(WithPropertyUsingUnderscoreInName.class));
assertThat(document).isEqualTo(new org.bson.Document("renamed", new org.bson.Document("$exists", true)));
}
@Test // GH-3601
void resolvesSimpleNestedFieldnameWithUnderscoresCorrectly() {
Query query = query(where("simple.fieldname_with_underscores").exists(true));
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(),
context.getPersistentEntity(WrapperAroundWithPropertyUsingUnderscoreInName.class));
assertThat(document)
.isEqualTo(new org.bson.Document("simple.fieldname_with_underscores", new org.bson.Document("$exists", true)));
}
@Test // GH-3601
void resolvesSimpleNestedMappedFieldnameWithUnderscoresCorrectly() {
Query query = query(where("simple.renamed_fieldname_with_underscores").exists(true));
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(),
context.getPersistentEntity(WrapperAroundWithPropertyUsingUnderscoreInName.class));
assertThat(document).isEqualTo(new org.bson.Document("simple.renamed", new org.bson.Document("$exists", true)));
}
@Test // GH-3601
void resolvesFieldNameWithUnderscoreOnNestedFieldnameWithUnderscoresCorrectly() {
Query query = query(where("double_underscore.fieldname_with_underscores").exists(true));
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(),
context.getPersistentEntity(WrapperAroundWithPropertyUsingUnderscoreInName.class));
assertThat(document).isEqualTo(
new org.bson.Document("double_underscore.fieldname_with_underscores", new org.bson.Document("$exists", true)));
}
@Test // GH-3601
void resolvesFieldNameWithUnderscoreOnNestedMappedFieldnameWithUnderscoresCorrectly() {
Query query = query(where("double_underscore.renamed_fieldname_with_underscores").exists(true));
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(),
context.getPersistentEntity(WrapperAroundWithPropertyUsingUnderscoreInName.class));
assertThat(document)
.isEqualTo(new org.bson.Document("double_underscore.renamed", new org.bson.Document("$exists", true)));
}
class WithDeepArrayNesting {
List<WithNestedArray> level0;
@@ -1194,4 +1265,17 @@ public class QueryMapperUnitTests {
this.value = value;
}
}
static class WrapperAroundWithPropertyUsingUnderscoreInName {
WithPropertyUsingUnderscoreInName simple;
WithPropertyUsingUnderscoreInName double_underscore;
}
static class WithPropertyUsingUnderscoreInName {
String fieldname_with_underscores;
@Field("renamed") String renamed_fieldname_with_underscores;
}
}

View File

@@ -34,6 +34,8 @@ import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
* @author Thomas Darimont
* @author Christoph Strobl
* @author Andreas Zink
* @author Clément Petit
* @author Mark Paluch
*/
public class CriteriaUnitTests {
@@ -310,9 +312,72 @@ public class CriteriaUnitTests {
@Test // DATAMONGO-2002
public void shouldEqualForSamePattern() {
Criteria left = new Criteria("field").regex("foo");
Criteria right = new Criteria("field").regex("foo");
assertThat(left).isEqualTo(right);
}
@Test // DATAMONGO-2002
public void shouldEqualForDocument() {
assertThat(new Criteria("field").is(new Document("one", 1).append("two", "two").append("null", null)))
.isEqualTo(new Criteria("field").is(new Document("one", 1).append("two", "two").append("null", null)));
assertThat(new Criteria("field").is(new Document("one", 1).append("two", "two").append("null", null)))
.isNotEqualTo(new Criteria("field").is(new Document("one", 1).append("two", "two")));
assertThat(new Criteria("field").is(new Document("one", 1).append("two", "two")))
.isNotEqualTo(new Criteria("field").is(new Document("one", 1).append("two", "two").append("null", null)));
assertThat(new Criteria("field").is(new Document("one", 1).append("null", null).append("two", "two")))
.isNotEqualTo(new Criteria("field").is(new Document("one", 1).append("two", "two").append("null", null)));
assertThat(new Criteria("field").is(new Document())).isNotEqualTo(new Criteria("field").is("foo"));
assertThat(new Criteria("field").is("foo")).isNotEqualTo(new Criteria("field").is(new Document()));
}
@Test // DATAMONGO-2002
public void shouldEqualForCollection() {
assertThat(new Criteria("field").is(Arrays.asList("foo", "bar")))
.isEqualTo(new Criteria("field").is(Arrays.asList("foo", "bar")));
assertThat(new Criteria("field").is(Arrays.asList("foo", 1)))
.isNotEqualTo(new Criteria("field").is(Arrays.asList("foo", "bar")));
assertThat(new Criteria("field").is(Collections.singletonList("foo")))
.isNotEqualTo(new Criteria("field").is(Arrays.asList("foo", "bar")));
assertThat(new Criteria("field").is(Arrays.asList("foo", "bar")))
.isNotEqualTo(new Criteria("field").is(Collections.singletonList("foo")));
assertThat(new Criteria("field").is(Arrays.asList("foo", "bar"))).isNotEqualTo(new Criteria("field").is("foo"));
assertThat(new Criteria("field").is("foo")).isNotEqualTo(new Criteria("field").is(Arrays.asList("foo", "bar")));
}
@Test // GH-3414
public void shouldEqualForSamePatternAndFlags() {
Criteria left = new Criteria("field").regex("foo", "iu");
Criteria right = new Criteria("field").regex("foo");
assertThat(left).isNotEqualTo(right);
}
@Test // GH-3414
public void shouldEqualForNestedPattern() {
Criteria left = new Criteria("a").orOperator(
new Criteria("foo").regex("value", "i"),
new Criteria("bar").regex("value")
);
Criteria right = new Criteria("a").orOperator(
new Criteria("foo").regex("value", "i"),
new Criteria("bar").regex("value")
);
assertThat(left).isEqualTo(right);
}
}

View File

@@ -20,15 +20,19 @@ import static org.springframework.data.domain.ExampleMatcher.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.Value;
import lombok.With;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import javax.annotation.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
@@ -44,10 +48,9 @@ import org.springframework.data.domain.Sort.Order;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.repository.support.ReactiveMongoRepositoryFactory;
import org.springframework.data.mongodb.repository.support.SimpleReactiveMongoRepository;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.util.ClassUtils;
/**
@@ -56,19 +59,22 @@ import org.springframework.util.ClassUtils;
* @author Mark Paluch
* @author Christoph Strobl
* @author Ruben J Garcia
* @author Clément Petit
*/
@RunWith(SpringRunner.class)
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:reactive-infrastructure.xml")
public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware, BeanFactoryAware {
@Autowired private ReactiveMongoTemplate template;
ReactiveMongoRepositoryFactory factory;
ClassLoader classLoader;
BeanFactory beanFactory;
ReactivePersonRepostitory repository;
private ReactiveMongoRepositoryFactory factory;
private ClassLoader classLoader;
private BeanFactory beanFactory;
private ReactivePersonRepository repository;
private ReactiveImmutablePersonRepository immutableRepository;
private ReactivePerson dave, oliver, carter, boyd, stefan, leroi, alicia;
private ImmutableReactivePerson keith, james, mariah;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
@@ -80,8 +86,8 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
this.beanFactory = beanFactory;
}
@Before
public void setUp() {
@BeforeEach
void setUp() {
factory = new ReactiveMongoRepositoryFactory(template);
factory.setRepositoryBaseClass(SimpleReactiveMongoRepository.class);
@@ -89,9 +95,11 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
factory.setBeanFactory(beanFactory);
factory.setEvaluationContextProvider(ReactiveQueryMethodEvaluationContextProvider.DEFAULT);
repository = factory.getRepository(ReactivePersonRepostitory.class);
repository = factory.getRepository(ReactivePersonRepository.class);
immutableRepository = factory.getRepository(ReactiveImmutablePersonRepository.class);
repository.deleteAll().as(StepVerifier::create).verifyComplete();
immutableRepository.deleteAll().as(StepVerifier::create).verifyComplete();
dave = new ReactivePerson("Dave", "Matthews", 42);
oliver = new ReactivePerson("Oliver August", "Matthews", 4);
@@ -100,6 +108,9 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
stefan = new ReactivePerson("Stefan", "Lessard", 34);
leroi = new ReactivePerson("Leroi", "Moore", 41);
alicia = new ReactivePerson("Alicia", "Keys", 30);
keith = new ImmutableReactivePerson(null, "Keith", "Urban", 53);
james = new ImmutableReactivePerson(null, "James", "Arthur", 33);
mariah = new ImmutableReactivePerson(null, "Mariah", "Carey", 51);
repository.saveAll(Arrays.asList(oliver, dave, carter, boyd, stefan, leroi, alicia)).as(StepVerifier::create) //
.expectNextCount(7) //
@@ -107,78 +118,78 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1444
public void existsByIdShouldReturnTrueForExistingObject() {
void existsByIdShouldReturnTrueForExistingObject() {
repository.existsById(dave.id).as(StepVerifier::create).expectNext(true).verifyComplete();
}
@Test // DATAMONGO-1444
public void existsByIdShouldReturnFalseForAbsentObject() {
void existsByIdShouldReturnFalseForAbsentObject() {
repository.existsById("unknown").as(StepVerifier::create).expectNext(false).verifyComplete();
}
@Test // DATAMONGO-1444
public void existsByMonoOfIdShouldReturnTrueForExistingObject() {
void existsByMonoOfIdShouldReturnTrueForExistingObject() {
repository.existsById(Mono.just(dave.id)).as(StepVerifier::create).expectNext(true).verifyComplete();
}
@Test // DATAMONGO-1712
public void existsByFluxOfIdShouldReturnTrueForExistingObject() {
void existsByFluxOfIdShouldReturnTrueForExistingObject() {
repository.existsById(Flux.just(dave.id, oliver.id)).as(StepVerifier::create).expectNext(true).verifyComplete();
}
@Test // DATAMONGO-1444
public void existsByEmptyMonoOfIdShouldReturnEmptyMono() {
void existsByEmptyMonoOfIdShouldReturnEmptyMono() {
repository.existsById(Mono.empty()).as(StepVerifier::create).verifyComplete();
}
@Test // DATAMONGO-1444
public void findByIdShouldReturnObject() {
void findByIdShouldReturnObject() {
repository.findById(dave.id).as(StepVerifier::create).expectNext(dave).verifyComplete();
}
@Test // DATAMONGO-1444
public void findByIdShouldCompleteWithoutValueForAbsentObject() {
void findByIdShouldCompleteWithoutValueForAbsentObject() {
repository.findById("unknown").as(StepVerifier::create).verifyComplete();
}
@Test // DATAMONGO-1444
public void findByIdByMonoOfIdShouldReturnTrueForExistingObject() {
void findByIdByMonoOfIdShouldReturnTrueForExistingObject() {
repository.findById(Mono.just(dave.id)).as(StepVerifier::create).expectNext(dave).verifyComplete();
}
@Test // DATAMONGO-1712
public void findByIdByFluxOfIdShouldReturnTrueForExistingObject() {
void findByIdByFluxOfIdShouldReturnTrueForExistingObject() {
repository.findById(Flux.just(dave.id, oliver.id)).as(StepVerifier::create).expectNext(dave).verifyComplete();
}
@Test // DATAMONGO-1444
public void findByIdByEmptyMonoOfIdShouldReturnEmptyMono() {
void findByIdByEmptyMonoOfIdShouldReturnEmptyMono() {
repository.findById(Mono.empty()).as(StepVerifier::create).verifyComplete();
}
@Test // DATAMONGO-1444
public void findAllShouldReturnAllResults() {
void findAllShouldReturnAllResults() {
repository.findAll().as(StepVerifier::create).expectNextCount(7).verifyComplete();
}
@Test // DATAMONGO-1444
public void findAllByIterableOfIdShouldReturnResults() {
void findAllByIterableOfIdShouldReturnResults() {
repository.findAllById(Arrays.asList(dave.id, boyd.id)).as(StepVerifier::create).expectNextCount(2)
.verifyComplete();
}
@Test // DATAMONGO-1444
public void findAllByPublisherOfIdShouldReturnResults() {
void findAllByPublisherOfIdShouldReturnResults() {
repository.findAllById(Flux.just(dave.id, boyd.id)).as(StepVerifier::create).expectNextCount(2).verifyComplete();
}
@Test // DATAMONGO-1444
public void findAllByEmptyPublisherOfIdShouldReturnResults() {
void findAllByEmptyPublisherOfIdShouldReturnResults() {
repository.findAllById(Flux.empty()).as(StepVerifier::create).verifyComplete();
}
@Test // DATAMONGO-1444
public void findAllWithSortShouldReturnResults() {
void findAllWithSortShouldReturnResults() {
repository.findAll(Sort.by(new Order(Direction.ASC, "age"))).as(StepVerifier::create) //
.expectNextCount(7) //
@@ -186,12 +197,12 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1444
public void countShouldReturnNumberOfRecords() {
void countShouldReturnNumberOfRecords() {
repository.count().as(StepVerifier::create).expectNext(7L).verifyComplete();
}
@Test // DATAMONGO-1444
public void insertEntityShouldInsertEntity() {
void insertEntityShouldInsertEntity() {
repository.deleteAll().as(StepVerifier::create).verifyComplete();
@@ -203,7 +214,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1444
public void insertShouldDeferredWrite() {
void insertShouldDeferredWrite() {
ReactivePerson person = new ReactivePerson("Homer", "Simpson", 36);
@@ -213,7 +224,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1444
public void insertIterableOfEntitiesShouldInsertEntity() {
void insertIterableOfEntitiesShouldInsertEntity() {
repository.deleteAll().as(StepVerifier::create).verifyComplete();
@@ -231,7 +242,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1444
public void insertPublisherOfEntitiesShouldInsertEntity() {
void insertPublisherOfEntitiesShouldInsertEntity() {
repository.deleteAll().as(StepVerifier::create).verifyComplete();
@@ -247,7 +258,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1444
public void saveEntityShouldUpdateExistingEntity() {
void saveEntityShouldUpdateExistingEntity() {
dave.setFirstname("Hello, Dave");
dave.setLastname("Bowman");
@@ -264,7 +275,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1444
public void saveEntityShouldInsertNewEntity() {
void saveEntityShouldInsertNewEntity() {
ReactivePerson person = new ReactivePerson("Homer", "Simpson", 36);
@@ -278,7 +289,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1444
public void saveIterableOfNewEntitiesShouldInsertEntity() {
void saveIterableOfNewEntitiesShouldInsertEntity() {
repository.deleteAll().as(StepVerifier::create).verifyComplete();
@@ -294,7 +305,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1444
public void saveIterableOfMixedEntitiesShouldInsertEntity() {
void saveIterableOfMixedEntitiesShouldInsertEntity() {
ReactivePerson person = new ReactivePerson("Homer", "Simpson", 36);
@@ -310,7 +321,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1444
public void savePublisherOfEntitiesShouldInsertEntity() {
void savePublisherOfEntitiesShouldInsertEntity() {
repository.deleteAll().as(StepVerifier::create).verifyComplete();
@@ -325,8 +336,20 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
assertThat(boyd.getId()).isNotNull();
}
@Test // GH-3609
void savePublisherOfImmutableEntitiesShouldInsertEntity() {
immutableRepository.deleteAll().as(StepVerifier::create).verifyComplete();
immutableRepository.saveAll(Flux.just(keith)).as(StepVerifier::create) //
.consumeNextWith(actual -> {
assertThat(actual.id).isNotNull();
}) //
.verifyComplete();
}
@Test // DATAMONGO-1444
public void deleteAllShouldRemoveEntities() {
void deleteAllShouldRemoveEntities() {
repository.deleteAll().as(StepVerifier::create).verifyComplete();
@@ -334,7 +357,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1444
public void deleteByIdShouldRemoveEntity() {
void deleteByIdShouldRemoveEntity() {
repository.deleteById(dave.id).as(StepVerifier::create).verifyComplete();
@@ -342,7 +365,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1712
public void deleteByIdUsingMonoShouldRemoveEntity() {
void deleteByIdUsingMonoShouldRemoveEntity() {
repository.deleteById(Mono.just(dave.id)).as(StepVerifier::create).verifyComplete();
@@ -350,7 +373,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1712
public void deleteByIdUsingFluxShouldRemoveEntity() {
void deleteByIdUsingFluxShouldRemoveEntity() {
repository.deleteById(Flux.just(dave.id, oliver.id)).as(StepVerifier::create).verifyComplete();
@@ -359,7 +382,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1444
public void deleteShouldRemoveEntity() {
void deleteShouldRemoveEntity() {
repository.delete(dave).as(StepVerifier::create).verifyComplete();
@@ -368,7 +391,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1444
public void deleteIterableOfEntitiesShouldRemoveEntities() {
void deleteIterableOfEntitiesShouldRemoveEntities() {
repository.deleteAll(Arrays.asList(dave, boyd)).as(StepVerifier::create).verifyComplete();
@@ -378,7 +401,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1444
public void deletePublisherOfEntitiesShouldRemoveEntities() {
void deletePublisherOfEntitiesShouldRemoveEntities() {
repository.deleteAll(Flux.just(dave, boyd)).as(StepVerifier::create).verifyComplete();
@@ -388,7 +411,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1619
public void findOneByExampleShouldReturnObject() {
void findOneByExampleShouldReturnObject() {
Example<ReactivePerson> example = Example.of(dave);
@@ -396,7 +419,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1619
public void findAllByExampleShouldReturnObjects() {
void findAllByExampleShouldReturnObjects() {
Example<ReactivePerson> example = Example.of(dave, matching().withIgnorePaths("id", "age", "firstname"));
@@ -404,7 +427,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1619
public void findAllByExampleAndSortShouldReturnObjects() {
void findAllByExampleAndSortShouldReturnObjects() {
Example<ReactivePerson> example = Example.of(dave, matching().withIgnorePaths("id", "age", "firstname"));
@@ -413,7 +436,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1619
public void countByExampleShouldCountObjects() {
void countByExampleShouldCountObjects() {
Example<ReactivePerson> example = Example.of(dave, matching().withIgnorePaths("id", "age", "firstname"));
@@ -421,7 +444,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1619
public void existsByExampleShouldReturnExisting() {
void existsByExampleShouldReturnExisting() {
Example<ReactivePerson> example = Example.of(dave, matching().withIgnorePaths("id", "age", "firstname"));
@@ -429,7 +452,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1619
public void existsByExampleShouldReturnNonExisting() {
void existsByExampleShouldReturnNonExisting() {
Example<ReactivePerson> example = Example.of(new ReactivePerson("foo", "bar", -1));
@@ -437,7 +460,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1619
public void findOneShouldEmitIncorrectResultSizeDataAccessExceptionWhenMoreThanOneElementFound() {
void findOneShouldEmitIncorrectResultSizeDataAccessExceptionWhenMoreThanOneElementFound() {
Example<ReactivePerson> example = Example.of(new ReactivePerson(null, "Matthews", -1),
matching().withIgnorePaths("age"));
@@ -446,19 +469,23 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@Test // DATAMONGO-1907
public void findOneByExampleWithoutResultShouldCompleteEmpty() {
void findOneByExampleWithoutResultShouldCompleteEmpty() {
Example<ReactivePerson> example = Example.of(new ReactivePerson("foo", "bar", -1));
repository.findOne(example).as(StepVerifier::create).verifyComplete();
}
interface ReactivePersonRepostitory extends ReactiveMongoRepository<ReactivePerson, String> {
interface ReactivePersonRepository extends ReactiveMongoRepository<ReactivePerson, String> {
Flux<ReactivePerson> findByLastname(String lastname);
}
interface ReactiveImmutablePersonRepository extends ReactiveMongoRepository<ImmutableReactivePerson, String> {
}
@Data
@NoArgsConstructor
static class ReactivePerson {
@@ -469,11 +496,30 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
String lastname;
int age;
public ReactivePerson(String firstname, String lastname, int age) {
ReactivePerson(String firstname, String lastname, int age) {
this.firstname = firstname;
this.lastname = lastname;
this.age = age;
}
}
@With
@Value
static class ImmutableReactivePerson {
@Id String id;
String firstname;
String lastname;
int age;
ImmutableReactivePerson(@Nullable String id, String firstname, String lastname, int age) {
this.id = id;
this.firstname = firstname;
this.lastname = lastname;
this.age = age;
}
}
}

View File

@@ -159,6 +159,14 @@ public class StringBasedAggregationUnitTests {
assertThat(executeAggregation("returnCollection").result).isEqualTo(expected);
}
@Test // GH-3623
public void returnNullWhenSingleResultIsNotPresent() {
when(aggregationResults.getMappedResults()).thenReturn(Collections.emptyList());
assertThat(executeAggregation("simpleReturnType").result).isNull();
}
@Test // DATAMONGO-2153
public void returnRawResultType() {
assertThat(executeAggregation("returnRawResultType").result).isEqualTo(aggregationResults);
@@ -312,6 +320,9 @@ public class StringBasedAggregationUnitTests {
@Aggregation(RAW_GROUP_BY_LASTNAME_STRING)
Page<Person> invalidPageReturnType(Pageable page);
@Aggregation(RAW_GROUP_BY_LASTNAME_STRING)
String simpleReturnType();
}
static class PersonAggregate {

View File

@@ -1,6 +1,53 @@
Spring Data MongoDB Changelog
=============================
Changes in version 3.1.8 (2021-04-14)
-------------------------------------
* #3623 - `@Aggregation` repository query method causes `NullPointerException` when the result is empty.
* #3601 - Criteria object not allowing to use field names with underscore in them.
* #3414 - Criteria or toEquals fail if contains regex [DATAMONGO-2559].
Changes in version 3.0.9.RELEASE (2021-04-14)
---------------------------------------------
* #3623 - `@Aggregation` repository query method causes `NullPointerException` when the result is empty.
* #3609 - SimpleReactiveMongoRepository#saveAll does not populate @Id property if it is immutable.
* #3414 - Criteria or toEquals fail if contains regex [DATAMONGO-2559].
Changes in version 3.1.7 (2021-03-31)
-------------------------------------
* #3613 - Use StringUtils.replace(…) instead of String.replaceAll(…) for mapKeyDotReplacement.
* #3609 - SimpleReactiveMongoRepository#saveAll does not populate @Id property if it is immutable.
Changes in version 3.2.0-RC1 (2021-03-31)
-----------------------------------------
* #3613 - Use StringUtils.replace(…) instead of String.replaceAll(…) for mapKeyDotReplacement.
* #3609 - SimpleReactiveMongoRepository#saveAll does not populate @Id property if it is immutable.
* #3600 - Rename Embedded annotation -> Unwrapped.
* #3583 - Support aggregation expression on fields projection.
Changes in version 3.2.0-M5 (2021-03-17)
----------------------------------------
* #3592 - Remove @Persistent from entity-scan include filters.
* #3590 - Embedded sharding keys are not correctly picked up from the shardKeySource Document.
* #3580 - Fix CustomConverter conversion lookup.
* #3579 - Upgrade to MongoDB Java Drivers 4.2.2.
* #3575 - Introduce ConversionContext and clean up MappingMongoConverter.
* #3573 - Json Schema section appears twice in reference documentation.
* #3571 - Introduce ConversionContext and clean up MappingMongoConverter.
* #3570 - Incorrect class casting cause ClassCastException when save java.util.Collection using MongoTemplate.
* #3568 - MongoSocketWriteException may be translated into DataAccessResourceFailureException.
* #3566 - Couldn't find PersistentEntity for type java.lang.Object when updating a field with suffix "class".
* #3552 - UpdateMapper drops numeric keys in Maps.
* #3395 - Derived findBy…IgnoreCaseIn query doesn't return expected results [DATAMONGO-2540].
* #3286 - Add possibility to use Collection<Criteria> as parameter in and/or/nor operators [DATAMONGO-2428].
* #2911 - ensureNotIterable in MongoTemplate only checks for array type [DATAMONGO-2044].
* #590 - DATAMONGO-2044 make ensureNotIterable actually check if object is iterable.
Changes in version 3.1.6 (2021-03-17)
-------------------------------------
* #3592 - Remove @Persistent from entity-scan include filters.
@@ -3363,6 +3410,11 @@ Repository

View File

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