Compare commits

..

16 Commits

Author SHA1 Message Date
Mark Paluch
2a81dc75a8 DATAMONGO-1816 - Release version 2.0.2 (Kay SR2). 2017-11-27 16:12:34 +01:00
Mark Paluch
58cd4c08ca DATAMONGO-1816 - Prepare 2.0.2 (Kay SR2). 2017-11-27 16:11:21 +01:00
Mark Paluch
344e019143 DATAMONGO-1816 - Updated changelog. 2017-11-27 16:11:15 +01:00
Mark Paluch
918b7e96bb DATAMONGO-1799 - Updated changelog. 2017-11-27 15:58:45 +01:00
Christoph Strobl
fce7a5c1cb DATAMONGO-1818 - Polishing.
Move overlapping/duplicate documentation into one place.

Original Pull Request: #512
2017-11-27 07:53:22 +01:00
Mark Paluch
dbd2de8e0f DATAMONGO-1818 - Reword tailable cursors documentation.
Fix reference to @Tailable annotation. Slightly reword documentation.

Original Pull Request: #512
2017-11-27 07:53:08 +01:00
Mark Paluch
0dbe331ab0 DATAMONGO-1823 - Polishing.
Replace constructor with lombok's RequiredArgsConstructor. Add Nullable annotation. Tiny reformatting. Align license header. Migrate test to AssertJ.

Original pull request: #517.
2017-11-22 14:33:20 +01:00
Christoph Strobl
846ebcd91d DATAMONGO-1823 - Emit ApplicationEvents using projecting find methods.
We now again emit application events when using finder methods that apply projection.

Original pull request: #517.
2017-11-22 14:33:20 +01:00
Oliver Gierke
9e0b5caeac DATAMONGO-1737 - BasicMongoPersistentEntity now correctly initializes comparator.
In BasicMongoPersistentEntity.verify() we now properly call the super method to make sure the comparators that honor the @Field's order value are initialized properly.
2017-11-17 14:55:00 +01:00
Mark Paluch
cf70f5e5eb DATAMONGO-1819 - Polishing.
Use native field names for NamedMongoScript query instead of relying on metadata-based mapping as NamedMongoScript is considered a simple top-level type.

Related pull request: #513.
2017-11-17 13:49:06 +01:00
Mark Paluch
331dc6df6f DATAMONGO-1821 - Fix method ambiguity in tests when compiling against MongoDB 3.6 2017-11-07 12:47:51 +01:00
Mark Paluch
a51dce2c90 DATAMONGO-1820 - Set Mongo's Feature Compatibility flag for TravisCI build to 3.4.
Apply setFeatureCompatibilityVersion to upgrade MongoDB to 3.4 features.
2017-11-06 10:28:10 +01:00
Mark Paluch
c0cf1aa95b DATAMONGO-1817 - Polishing.
Remove blank line.

Original pull request: #510.
2017-11-06 10:02:35 +01:00
Sola
7104ffa543 DATAMONGO-1817 - Align nullability in Kotlin MongoOperationsExtensions with Java API.
Return types in MongoOperationsExtensions are now aligned to the nullability of MongoOperations.

Original pull request: #510.
2017-11-06 10:02:35 +01:00
Oliver Gierke
28d2fb6680 DATAMONGO-1793 - After release cleanups. 2017-10-27 15:50:48 +02:00
Oliver Gierke
140e26946f DATAMONGO-1793 - Prepare next development iteration. 2017-10-27 15:50:45 +02:00
17 changed files with 240 additions and 195 deletions

View File

@@ -31,6 +31,8 @@ cache:
directories:
- $HOME/.m2
install: true
install:
- |-
mongo admin --eval "db.adminCommand({setFeatureCompatibilityVersion: '3.4'});"
script: "mvn clean dependency:list test -P${PROFILE} -Dsort"

View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>2.0.1.RELEASE</version>
<version>2.0.2.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.0.1.RELEASE</version>
<version>2.0.2.RELEASE</version>
</parent>
<modules>
@@ -27,7 +27,7 @@
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>2.0.1.RELEASE</springdata.commons>
<springdata.commons>2.0.2.RELEASE</springdata.commons>
<mongo>3.5.0</mongo>
<mongo.reactivestreams>1.6.0</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>2.0.1.RELEASE</version>
<version>2.0.2.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>2.0.1.RELEASE</version>
<version>2.0.2.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -49,7 +49,7 @@
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>2.0.1.RELEASE</version>
<version>2.0.2.RELEASE</version>
</dependency>
<!-- reactive -->

View File

@@ -13,7 +13,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>2.0.1.RELEASE</version>
<version>2.0.2.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>2.0.1.RELEASE</version>
<version>2.0.2.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -43,9 +43,10 @@ import com.mongodb.client.MongoDatabase;
/**
* Default implementation of {@link ScriptOperations} capable of saving and executing {@link ServerSideJavaScript}.
*
*
* @author Christoph Strobl
* @author Oliver Gierke
* @author Mark Paluch
* @since 1.7
*/
class DefaultScriptOperations implements ScriptOperations {
@@ -141,7 +142,7 @@ class DefaultScriptOperations implements ScriptOperations {
Assert.hasText(scriptName, "ScriptName must not be null or empty!");
return mongoOperations.exists(query(where("name").is(scriptName)), NamedMongoScript.class, SCRIPT_COLLECTION_NAME);
return mongoOperations.exists(query(where("_id").is(scriptName)), NamedMongoScript.class, SCRIPT_COLLECTION_NAME);
}
/*
@@ -190,7 +191,7 @@ class DefaultScriptOperations implements ScriptOperations {
* Generate a valid name for the {@literal JavaScript}. MongoDB requires an id of type String for scripts. Calling
* scripts having {@link ObjectId} as id fails. Therefore we create a random UUID without {@code -} (as this won't
* work) an prefix the result with {@link #SCRIPT_NAME_PREFIX}.
*
*
* @return
*/
private static String generateScriptName() {

View File

@@ -2771,31 +2771,26 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
* @author Oliver Gierke
* @author Christoph Strobl
*/
@RequiredArgsConstructor
private class ReadDocumentCallback<T> implements DocumentCallback<T> {
private final EntityReader<? super T, Bson> reader;
private final Class<T> type;
private final @NonNull EntityReader<? super T, Bson> reader;
private final @NonNull Class<T> type;
private final String collectionName;
public ReadDocumentCallback(EntityReader<? super T, Bson> reader, Class<T> type, String collectionName) {
Assert.notNull(reader, "EntityReader must not be null!");
Assert.notNull(type, "Entity type must not be null!");
this.reader = reader;
this.type = type;
this.collectionName = collectionName;
}
@Nullable
public T doWith(Document object) {
public T doWith(@Nullable Document object) {
if (null != object) {
maybeEmitEvent(new AfterLoadEvent<T>(object, type, collectionName));
}
T source = reader.read(type, object);
if (null != source) {
maybeEmitEvent(new AfterConvertEvent<T>(object, source, collectionName));
}
return source;
}
}
@@ -2830,10 +2825,15 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Class<?> typeToRead = targetType.isInterface() || targetType.isAssignableFrom(entityType) ? entityType
: targetType;
if (null != object) {
maybeEmitEvent(new AfterLoadEvent<T>(object, targetType, collectionName));
}
Object source = reader.read(typeToRead, object);
Object result = targetType.isInterface() ? projectionFactory.createProjection(targetType, source) : source;
if (result == null) {
if (null != result) {
maybeEmitEvent(new AfterConvertEvent<>(object, result, collectionName));
}
@@ -2986,7 +2986,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
T doWith = delegate.doWith(content);
return new GeoResult<T>(doWith, new Distance(distance, metric));
return new GeoResult<>(doWith, new Distance(distance, metric));
}
}

View File

@@ -149,6 +149,8 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
@Override
public void verify() {
super.verify();
verifyFieldUniqueness();
verifyFieldTypes();
}

View File

@@ -59,7 +59,7 @@ inline fun <reified T : Any> MongoOperations.getCollectionName(): String =
* @author Sebastien Deleuze
* @since 2.0
*/
inline fun <reified T : Any> MongoOperations.execute(action: CollectionCallback<T>): T =
inline fun <reified T : Any> MongoOperations.execute(action: CollectionCallback<T>): T? =
execute(T::class.java, action)
/**
@@ -278,7 +278,7 @@ inline fun <reified T : Any> MongoOperations.geoNear(near: NearQuery, collection
* @author Sebastien Deleuze
* @since 2.0
*/
inline fun <reified T : Any> MongoOperations.findOne(query: Query, collectionName: String? = null): T =
inline fun <reified T : Any> MongoOperations.findOne(query: Query, collectionName: String? = null): T? =
if (collectionName != null) findOne(query, T::class.java, collectionName) else findOne(query, T::class.java)
/**
@@ -318,7 +318,7 @@ inline fun <reified T : Any> MongoOperations.find(query: Query, collectionName:
* @author Sebastien Deleuze
* @since 2.0
*/
inline fun <reified T : Any> MongoOperations.findById(id: Any, collectionName: String? = null): T =
inline fun <reified T : Any> MongoOperations.findById(id: Any, collectionName: String? = null): T? =
if (collectionName != null) findById(id, T::class.java, collectionName)
else findById(id, T::class.java)
@@ -328,7 +328,7 @@ inline fun <reified T : Any> MongoOperations.findById(id: Any, collectionName: S
* @author Sebastien Deleuze
* @since 2.0
*/
inline fun <reified T : Any> MongoOperations.findAndModify(query: Query, update: Update, options: FindAndModifyOptions, collectionName: String? = null): T =
inline fun <reified T : Any> MongoOperations.findAndModify(query: Query, update: Update, options: FindAndModifyOptions, collectionName: String? = null): T? =
if (collectionName != null) findAndModify(query, update, options, T::class.java, collectionName)
else findAndModify(query, update, options, T::class.java)
@@ -338,7 +338,7 @@ inline fun <reified T : Any> MongoOperations.findAndModify(query: Query, update:
* @author Sebastien Deleuze
* @since 2.0
*/
inline fun <reified T : Any> MongoOperations.findAndRemove(query: Query, collectionName: String? = null): T =
inline fun <reified T : Any> MongoOperations.findAndRemove(query: Query, collectionName: String? = null): T? =
if (collectionName != null) findAndRemove(query, T::class.java, collectionName)
else findAndRemove(query, T::class.java)
@@ -414,7 +414,6 @@ inline fun <reified T : Any> MongoOperations.updateFirst(query: Query, update: U
if (collectionName != null) updateFirst(query, update, T::class.java, collectionName)
else updateFirst(query, update, T::class.java)
/**
* Extension for [MongoOperations.updateMulti] providing a [KClass] based variant.
*

View File

@@ -129,7 +129,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
when(db.runCommand(Mockito.any(), Mockito.any(Class.class))).thenReturn(commandResultDocument);
when(collection.find(Mockito.any(org.bson.Document.class))).thenReturn(findIterable);
when(collection.mapReduce(Mockito.any(), Mockito.any())).thenReturn(mapReduceIterable);
when(collection.count(any(), any())).thenReturn(1L);
when(collection.count(any(Bson.class), any(CountOptions.class))).thenReturn(1L);
when(findIterable.projection(Mockito.any())).thenReturn(findIterable);
when(findIterable.sort(Mockito.any(org.bson.Document.class))).thenReturn(findIterable);
when(findIterable.modifiers(Mockito.any(org.bson.Document.class))).thenReturn(findIterable);

View File

@@ -15,14 +15,17 @@
*/
package org.springframework.data.mongodb.core.mapping;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.junit.Assert.assertThat;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.junit.Before;
@@ -186,6 +189,19 @@ public class BasicMongoPersistentPropertyUnitTests {
assertThat(property.getFieldName(), is("myField"));
}
@Test // DATAMONGO-1737
public void honorsFieldOrderWhenIteratingOverProperties() {
MongoMappingContext context = new MongoMappingContext();
BasicMongoPersistentEntity<?> entity = context.getPersistentEntity(Sample.class);
List<String> properties = new ArrayList<>();
entity.doWithProperties((MongoPersistentProperty property) -> properties.add(property.getName()));
assertThat(properties).containsExactly("first", "second", "third");
}
private MongoPersistentProperty getPropertyFor(Field field) {
return getPropertyFor(entity, field);
}
@@ -213,6 +229,13 @@ public class BasicMongoPersistentPropertyUnitTests {
@org.springframework.data.mongodb.core.mapping.Field(order = -20) String ssn;
}
class Sample {
@org.springframework.data.mongodb.core.mapping.Field(order = 2) String second;
@org.springframework.data.mongodb.core.mapping.Field(order = 3) String third;
@org.springframework.data.mongodb.core.mapping.Field(order = 1) String first;
}
enum UppercaseFieldNamingStrategy implements FieldNamingStrategy {
INSTANCE;

View File

@@ -1,11 +1,11 @@
/*
* Copyright (c) 2011-2017 by the original author(s).
* Copyright 2011-2017 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
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://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,
@@ -15,23 +15,23 @@
*/
package org.springframework.data.mongodb.core.mapping.event;
import static org.hamcrest.collection.IsCollectionWithSize.*;
import static org.hamcrest.core.Is.*;
import static org.hamcrest.core.IsEqual.*;
import static org.junit.Assert.*;
import static org.springframework.data.mongodb.core.DocumentTestUtils.assertTypeHint;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.mongodb.core.DocumentTestUtils.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import java.net.UnknownHostException;
import lombok.Data;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@@ -41,19 +41,17 @@ import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.PersonPojoStringId;
import com.mongodb.DB;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
import com.mongodb.WriteConcern;
import lombok.Data;
import com.mongodb.client.MongoDatabase;
/**
* Integration test for Mapping Events.
*
*
* @author Mark Pollack
* @author Christoph Strobl
* @author Jordi Llach
* @author Mark Paluch
*/
public class ApplicationContextEventTests {
@@ -64,28 +62,40 @@ public class ApplicationContextEventTests {
private final String[] collectionsToDrop = new String[] { COLLECTION_NAME, ROOT_COLLECTION_NAME,
RELATED_COLLECTION_NAME };
private static MongoClient mongo;
private ApplicationContext applicationContext;
private MongoTemplate template;
private SimpleMappingEventListener simpleMappingEventListener;
private SimpleMappingEventListener listener;
@BeforeClass
public static void beforeClass() {
mongo = new MongoClient();
}
@AfterClass
public static void afterClass() {
mongo.close();
}
@Before
public void setUp() throws Exception {
public void setUp() {
cleanDb();
applicationContext = new AnnotationConfigApplicationContext(ApplicationContextEventTestsAppConfig.class);
template = applicationContext.getBean(MongoTemplate.class);
template.setWriteConcern(WriteConcern.FSYNC_SAFE);
simpleMappingEventListener = applicationContext.getBean(SimpleMappingEventListener.class);
listener = applicationContext.getBean(SimpleMappingEventListener.class);
}
@After
public void cleanUp() throws Exception {
public void cleanUp() {
cleanDb();
}
private void cleanDb() throws UnknownHostException {
private void cleanDb() {
Mongo mongo = new MongoClient();
DB db = mongo.getDB("database");
MongoDatabase db = mongo.getDatabase("database");
for (String coll : collectionsToDrop) {
db.getCollection(coll).drop();
}
@@ -98,23 +108,23 @@ public class ApplicationContextEventTests {
PersonBeforeSaveListener personBeforeSaveListener = applicationContext.getBean(PersonBeforeSaveListener.class);
AfterSaveListener afterSaveListener = applicationContext.getBean(AfterSaveListener.class);
assertEquals(0, personBeforeSaveListener.seenEvents.size());
assertEquals(0, afterSaveListener.seenEvents.size());
assertThat(personBeforeSaveListener.seenEvents).isEmpty();
assertThat(afterSaveListener.seenEvents).isEmpty();
assertEquals(0, simpleMappingEventListener.onBeforeSaveEvents.size());
assertEquals(0, simpleMappingEventListener.onAfterSaveEvents.size());
assertThat(listener.onBeforeSaveEvents).isEmpty();
assertThat(listener.onAfterSaveEvents).isEmpty();
PersonPojoStringId p = new PersonPojoStringId("1", "Text");
template.insert(p);
assertEquals(1, personBeforeSaveListener.seenEvents.size());
assertEquals(1, afterSaveListener.seenEvents.size());
assertThat(personBeforeSaveListener.seenEvents).hasSize(1);
assertThat(afterSaveListener.seenEvents).hasSize(1);
assertEquals(1, simpleMappingEventListener.onBeforeSaveEvents.size());
assertEquals(1, simpleMappingEventListener.onAfterSaveEvents.size());
assertThat(listener.onBeforeSaveEvents).hasSize(1);
assertThat(listener.onAfterSaveEvents).hasSize(1);
assertEquals(COLLECTION_NAME, simpleMappingEventListener.onBeforeSaveEvents.get(0).getCollectionName());
assertEquals(COLLECTION_NAME, simpleMappingEventListener.onAfterSaveEvents.get(0).getCollectionName());
assertThat(listener.onBeforeSaveEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME);
assertThat(listener.onAfterSaveEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME);
Assert.assertTrue(personBeforeSaveListener.seenEvents.get(0) instanceof BeforeSaveEvent<?>);
Assert.assertTrue(afterSaveListener.seenEvents.get(0) instanceof AfterSaveEvent<?>);
@@ -142,14 +152,14 @@ public class ApplicationContextEventTests {
template.findOne(query(where("id").is(entity.getId())), PersonPojoStringId.class);
assertThat(simpleMappingEventListener.onAfterLoadEvents.size(), is(1));
assertThat(simpleMappingEventListener.onAfterLoadEvents.get(0).getCollectionName(), is(COLLECTION_NAME));
assertThat(listener.onAfterLoadEvents).hasSize(1);
assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME);
assertThat(simpleMappingEventListener.onBeforeConvertEvents.size(), is(1));
assertThat(simpleMappingEventListener.onBeforeConvertEvents.get(0).getCollectionName(), is(COLLECTION_NAME));
assertThat(listener.onBeforeConvertEvents).hasSize(1);
assertThat(listener.onBeforeConvertEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME);
assertThat(simpleMappingEventListener.onAfterConvertEvents.size(), is(1));
assertThat(simpleMappingEventListener.onAfterConvertEvents.get(0).getCollectionName(), is(COLLECTION_NAME));
assertThat(listener.onAfterConvertEvents).hasSize(1);
assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME);
}
@Test // DATAMONGO-1256
@@ -160,14 +170,14 @@ public class ApplicationContextEventTests {
template.aggregate(Aggregation.newAggregation(Aggregation.project("text")), PersonPojoStringId.class,
PersonPojoStringId.class);
assertThat(simpleMappingEventListener.onAfterLoadEvents.size(), is(1));
assertThat(simpleMappingEventListener.onAfterLoadEvents.get(0).getCollectionName(), is(COLLECTION_NAME));
assertThat(listener.onAfterLoadEvents).hasSize(1);
assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME);
assertThat(simpleMappingEventListener.onBeforeConvertEvents.size(), is(1));
assertThat(simpleMappingEventListener.onBeforeConvertEvents.get(0).getCollectionName(), is(COLLECTION_NAME));
assertThat(listener.onBeforeConvertEvents).hasSize(1);
assertThat(listener.onBeforeConvertEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME);
assertThat(simpleMappingEventListener.onAfterConvertEvents.size(), is(1));
assertThat(simpleMappingEventListener.onAfterConvertEvents.get(0).getCollectionName(), is(COLLECTION_NAME));
assertThat(listener.onAfterConvertEvents).hasSize(1);
assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME);
}
@Test // DATAMONGO-1256
@@ -178,15 +188,15 @@ public class ApplicationContextEventTests {
template.remove(entity);
assertThat(simpleMappingEventListener.onBeforeDeleteEvents.size(), is(1));
assertThat(simpleMappingEventListener.onBeforeDeleteEvents.get(0).getCollectionName(), is(COLLECTION_NAME));
assertThat(listener.onBeforeDeleteEvents).hasSize(1);
assertThat(listener.onBeforeDeleteEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME);
assertThat(simpleMappingEventListener.onAfterDeleteEvents.size(), is(1));
assertThat(simpleMappingEventListener.onAfterDeleteEvents.get(0).getCollectionName(), is(COLLECTION_NAME));
assertThat(listener.onAfterDeleteEvents).hasSize(1);
assertThat(listener.onAfterDeleteEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME);
}
@Test // DATAMONGO-1271
public void publishesAfterLoadAndAfterConvertEventsForDBRef() throws Exception {
public void publishesAfterLoadAndAfterConvertEventsForDBRef() {
Related ref1 = new Related(2L, "related desc1");
@@ -200,21 +210,17 @@ public class ApplicationContextEventTests {
template.findOne(query(where("id").is(source.getId())), Root.class);
assertThat(simpleMappingEventListener.onAfterLoadEvents, hasSize(2));
assertThat(simpleMappingEventListener.onAfterLoadEvents.get(0).getCollectionName(),
is(equalTo(ROOT_COLLECTION_NAME)));
assertThat(simpleMappingEventListener.onAfterLoadEvents.get(1).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(listener.onAfterLoadEvents).hasSize(2);
assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME);
assertThat(listener.onAfterLoadEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
assertThat(simpleMappingEventListener.onAfterConvertEvents, hasSize(2));
assertThat(simpleMappingEventListener.onAfterConvertEvents.get(0).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(simpleMappingEventListener.onAfterConvertEvents.get(1).getCollectionName(),
is(equalTo(ROOT_COLLECTION_NAME)));
assertThat(listener.onAfterConvertEvents).hasSize(2);
assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
assertThat(listener.onAfterConvertEvents.get(1).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME);
}
@Test // DATAMONGO-1271
public void publishesAfterLoadAndAfterConvertEventsForLazyLoadingDBRef() throws Exception {
public void publishesAfterLoadAndAfterConvertEventsForLazyLoadingDBRef() {
Related ref1 = new Related(2L, "related desc1");
@@ -228,27 +234,23 @@ public class ApplicationContextEventTests {
Root target = template.findOne(query(where("id").is(source.getId())), Root.class);
assertThat(simpleMappingEventListener.onAfterLoadEvents, hasSize(1));
assertThat(simpleMappingEventListener.onAfterLoadEvents.get(0).getCollectionName(),
is(equalTo(ROOT_COLLECTION_NAME)));
assertThat(listener.onAfterLoadEvents).hasSize(1);
assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME);
assertThat(simpleMappingEventListener.onAfterConvertEvents, hasSize(1));
assertThat(simpleMappingEventListener.onAfterConvertEvents.get(0).getCollectionName(),
is(equalTo(ROOT_COLLECTION_NAME)));
assertThat(listener.onAfterConvertEvents).hasSize(1);
assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME);
target.getLazyReference().getDescription();
assertThat(simpleMappingEventListener.onAfterLoadEvents, hasSize(2));
assertThat(simpleMappingEventListener.onAfterLoadEvents.get(1).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(listener.onAfterLoadEvents).hasSize(2);
assertThat(listener.onAfterLoadEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
assertThat(simpleMappingEventListener.onAfterConvertEvents, hasSize(2));
assertThat(simpleMappingEventListener.onAfterConvertEvents.get(1).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(listener.onAfterConvertEvents).hasSize(2);
assertThat(listener.onAfterConvertEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
}
@Test // DATAMONGO-1271
public void publishesAfterLoadAndAfterConvertEventsForListOfDBRef() throws Exception {
public void publishesAfterLoadAndAfterConvertEventsForListOfDBRef() {
List<Related> references = Arrays.asList(new Related(20L, "ref 1"), new Related(30L, "ref 2"));
@@ -262,25 +264,19 @@ public class ApplicationContextEventTests {
template.findOne(query(where("id").is(source.getId())), Root.class);
assertThat(simpleMappingEventListener.onAfterLoadEvents, hasSize(3));
assertThat(simpleMappingEventListener.onAfterLoadEvents.get(0).getCollectionName(),
is(equalTo(ROOT_COLLECTION_NAME)));
assertThat(simpleMappingEventListener.onAfterLoadEvents.get(1).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(simpleMappingEventListener.onAfterLoadEvents.get(2).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(listener.onAfterLoadEvents).hasSize(3);
assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME);
assertThat(listener.onAfterLoadEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
assertThat(listener.onAfterLoadEvents.get(2).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
assertThat(simpleMappingEventListener.onAfterConvertEvents, hasSize(3));
assertThat(simpleMappingEventListener.onAfterConvertEvents.get(0).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(simpleMappingEventListener.onAfterConvertEvents.get(1).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(simpleMappingEventListener.onAfterConvertEvents.get(2).getCollectionName(),
is(equalTo(ROOT_COLLECTION_NAME)));
assertThat(listener.onAfterConvertEvents).hasSize(3);
assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
assertThat(listener.onAfterConvertEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
assertThat(listener.onAfterConvertEvents.get(2).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME);
}
@Test // DATAMONGO-1271
public void publishesAfterLoadAndAfterConvertEventsForLazyLoadingListOfDBRef() throws Exception {
public void publishesAfterLoadAndAfterConvertEventsForLazyLoadingListOfDBRef() {
List<Related> references = Arrays.asList(new Related(20L, "ref 1"), new Related(30L, "ref 2"));
@@ -294,30 +290,24 @@ public class ApplicationContextEventTests {
Root target = template.findOne(query(where("id").is(source.getId())), Root.class);
assertThat(simpleMappingEventListener.onAfterLoadEvents, hasSize(1));
assertThat(simpleMappingEventListener.onAfterLoadEvents.get(0).getCollectionName(),
is(equalTo(ROOT_COLLECTION_NAME)));
assertThat(simpleMappingEventListener.onAfterConvertEvents, hasSize(1));
assertThat(simpleMappingEventListener.onAfterConvertEvents.get(0).getCollectionName(),
is(equalTo(ROOT_COLLECTION_NAME)));
assertThat(listener.onAfterLoadEvents).hasSize(1);
assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME);
assertThat(listener.onAfterConvertEvents).hasSize(1);
assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME);
target.getLazyListOfReferences().size();
assertThat(simpleMappingEventListener.onAfterLoadEvents, hasSize(3));
assertThat(simpleMappingEventListener.onAfterConvertEvents, hasSize(3));
assertThat(listener.onAfterLoadEvents).hasSize(3);
assertThat(listener.onAfterConvertEvents).hasSize(3);
assertThat(simpleMappingEventListener.onAfterLoadEvents.get(1).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(simpleMappingEventListener.onAfterLoadEvents.get(2).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(simpleMappingEventListener.onAfterConvertEvents.get(1).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(simpleMappingEventListener.onAfterConvertEvents.get(2).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(listener.onAfterLoadEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
assertThat(listener.onAfterLoadEvents.get(2).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
assertThat(listener.onAfterConvertEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
assertThat(listener.onAfterConvertEvents.get(2).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
}
@Test // DATAMONGO-1271
public void publishesAfterLoadAndAfterConvertEventsForMapOfDBRef() throws Exception {
public void publishesAfterLoadAndAfterConvertEventsForMapOfDBRef() {
Map<String, Related> references = new LinkedHashMap<String, Related>();
references.put("ref-1", new Related(20L, "ref 1"));
@@ -333,25 +323,19 @@ public class ApplicationContextEventTests {
template.findOne(query(where("id").is(source.getId())), Root.class);
assertThat(simpleMappingEventListener.onAfterLoadEvents, hasSize(3));
assertThat(simpleMappingEventListener.onAfterLoadEvents.get(0).getCollectionName(),
is(equalTo(ROOT_COLLECTION_NAME)));
assertThat(simpleMappingEventListener.onAfterLoadEvents.get(1).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(simpleMappingEventListener.onAfterLoadEvents.get(2).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(listener.onAfterLoadEvents).hasSize(3);
assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME);
assertThat(listener.onAfterLoadEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
assertThat(listener.onAfterLoadEvents.get(2).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
assertThat(simpleMappingEventListener.onAfterConvertEvents, hasSize(3));
assertThat(simpleMappingEventListener.onAfterConvertEvents.get(0).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(simpleMappingEventListener.onAfterConvertEvents.get(1).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(simpleMappingEventListener.onAfterConvertEvents.get(2).getCollectionName(),
is(equalTo(ROOT_COLLECTION_NAME)));
assertThat(listener.onAfterConvertEvents).hasSize(3);
assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
assertThat(listener.onAfterConvertEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
assertThat(listener.onAfterConvertEvents.get(2).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME);
}
@Test // DATAMONGO-1271
public void publishesAfterLoadAndAfterConvertEventsForLazyLoadingMapOfDBRef() throws Exception {
public void publishesAfterLoadAndAfterConvertEventsForLazyLoadingMapOfDBRef() {
Map<String, Related> references = new LinkedHashMap<String, Related>();
references.put("ref-1", new Related(20L, "ref 1"));
@@ -367,36 +351,48 @@ public class ApplicationContextEventTests {
Root target = template.findOne(query(where("id").is(source.getId())), Root.class);
assertThat(simpleMappingEventListener.onAfterLoadEvents, hasSize(1));
assertThat(simpleMappingEventListener.onAfterLoadEvents.get(0).getCollectionName(),
is(equalTo(ROOT_COLLECTION_NAME)));
assertThat(listener.onAfterLoadEvents).hasSize(1);
assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME);
assertThat(simpleMappingEventListener.onAfterConvertEvents, hasSize(1));
assertThat(simpleMappingEventListener.onAfterConvertEvents.get(0).getCollectionName(),
is(equalTo(ROOT_COLLECTION_NAME)));
assertThat(listener.onAfterConvertEvents).hasSize(1);
assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME);
target.getLazyMapOfReferences().size();
assertThat(simpleMappingEventListener.onAfterLoadEvents, hasSize(3));
assertThat(simpleMappingEventListener.onAfterLoadEvents.get(1).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(simpleMappingEventListener.onAfterLoadEvents.get(2).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(listener.onAfterLoadEvents).hasSize(3);
assertThat(listener.onAfterLoadEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
assertThat(listener.onAfterLoadEvents.get(2).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
assertThat(simpleMappingEventListener.onAfterConvertEvents, hasSize(3));
assertThat(simpleMappingEventListener.onAfterConvertEvents.get(1).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(simpleMappingEventListener.onAfterConvertEvents.get(2).getCollectionName(),
is(equalTo(RELATED_COLLECTION_NAME)));
assertThat(listener.onAfterConvertEvents).hasSize(3);
assertThat(listener.onAfterConvertEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
assertThat(listener.onAfterConvertEvents.get(2).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME);
}
@Test // DATAMONGO-1823
public void publishesAfterConvertEventForFindQueriesUsingProjections() {
PersonPojoStringId entity = new PersonPojoStringId("1", "Text");
template.insert(entity);
template.query(PersonPojoStringId.class).matching(query(where("id").is(entity.getId()))).all();
assertThat(listener.onAfterLoadEvents).hasSize(1);
assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME);
assertThat(listener.onBeforeConvertEvents).hasSize(1);
assertThat(listener.onBeforeConvertEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME);
assertThat(listener.onAfterConvertEvents).hasSize(1);
assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME);
}
private void comparePersonAndDocument(PersonPojoStringId p, PersonPojoStringId p2, org.bson.Document document) {
assertEquals(p.getId(), p2.getId());
assertEquals(p.getText(), p2.getText());
assertThat(p2.getId()).isEqualTo(p.getId());
assertThat(p2.getText()).isEqualTo(p.getText());
assertEquals("1", document.get("_id"));
assertEquals("Text", document.get("text"));
assertThat(document.get("_id")).isEqualTo("1");
assertThat(document.get("text")).isEqualTo("Text");
assertTypeHint(document, PersonPojoStringId.class);
}

View File

@@ -190,10 +190,30 @@ public interface PersonRepository extends ReactiveMongoRepository<Person, String
[[mongo.reactive.repositories.infinite-streams]]
== Infinite Streams with Tailable Cursors
By default, MongoDB will automatically close a cursor when the client has exhausted all results in the cursor. Closing a cursors turns a Stream into a finite stream. However, for capped collections you may use a https://docs.mongodb.com/manual/core/tailable-cursors/[Tailable Cursor] that remains open after the client exhausts the results in the initial cursor. Using tailable Cursors with a reactive approach allows construction of infinite streams. A tailable Cursor remains open until it's closed. It emits data as data arrives in a capped collection. Using Tailable Cursors with Collections is not possible as its result would never complete.
By default, MongoDB will automatically close a cursor when the client exhausts all results supplied by the cursor. Closing a cursor on exhaustion turns a stream into a finite stream. For https://docs.mongodb.com/manual/core/capped-collections/[capped collections] you may use a https://docs.mongodb.com/manual/core/tailable-cursors/[Tailable Cursor] that remains open after the client consumes all initially returned data. Using tailable cursors with a reactive data types allows construction of infinite streams. A tailable cursor remains open until it is closed externally. It emits data as new documents arrive in a capped collection.
Spring Data MongoDB Reactive Repository support supports infinite streams by annotating a query method with `@TailableCursor`. This works for methods returning `Flux` or `Observable` wrapper types.
Tailable cursors may become dead, or invalid, if either the query returns no match or the cursor returns the document at the "end" of the collection and then the application deletes that document.
.Infinite Stream queries with ReactiveMongoOperations
====
[source,java]
----
Flux<Person> stream = template.tail(query(where("name").is("Joe")), Person.class);
Disposable subscription = stream.doOnNext(person -> System.out.println(person)).subscribe();
// …
// Later: Dispose the subscription to close the stream
subscription.dispose();
----
====
Spring Data MongoDB Reactive repositories support infinite streams by annotating a query method with `@Tailable`. This works for methods returning `Flux` and other reactive types capable of emitting multiple elements.
.Infinite Stream queries with ReactiveMongoRepository
====
[source,java]
----
@@ -210,6 +230,9 @@ Disposable subscription = stream.doOnNext(System.out::println).subscribe();
// …
// Later: Dispose the stream
// Later: Dispose the subscription to close the stream
subscription.dispose();
----
====
TIP: Capped collections can be created via `MongoOperations.createCollection`. Just provide the required `CollectionOptions.empty().capped()...`

View File

@@ -457,24 +457,6 @@ NOTE: This example is meant to show the use of save, update and remove operation
The query syntax used in the example is explained in more detail in the section <<mongo.query,Querying Documents>>. Additional documentation can be found in <<mongo-template, the blocking MongoTemplate>> section.
[[mongo.reactive.tailcursors]]
== Infinite Streams
By default, MongoDB will automatically close a cursor when the client has exhausted all results in the cursor. Closing a cursors turns a Stream into a finite stream. However, for capped collections you may use a https://docs.mongodb.com/manual/core/tailable-cursors/[Tailable Cursor] that remains open after the client exhausts the results in the initial cursor. Using Tailable Cursors with a reactive approach allows construction of infinite streams. A Tailable Cursor remains open until it's closed. It emits data as data arrives in a capped collection. Using Tailable Cursors with Collections is not possible as its result would never complete.
[source,java]
----
Flux<Person> stream = template.tail(query(where("name").is("Joe")), Person.class);
Disposable subscription = stream.doOnNext(person -> System.out.println(person)).subscribe();
// …
// Later: Dispose the stream
subscription.dispose();
----
[[mongo.reactive.executioncallback]]
== Execution callbacks

View File

@@ -1,6 +1,23 @@
Spring Data MongoDB Changelog
=============================
Changes in version 2.0.2.RELEASE (2017-11-27)
---------------------------------------------
* DATAMONGO-1823 - AfterConvertEvent is not published when using custom methods in repository interface.
* DATAMONGO-1821 - Fix method ambiguity in tests when compiling against MongoDB 3.6.
* DATAMONGO-1820 - Investigate failing TravisCI build.
* DATAMONGO-1818 - Reference documentation mentions @TailableCursor instead of @Tailable.
* DATAMONGO-1817 - Kotlin extensions should return nullable types.
* DATAMONGO-1816 - Release 2.0.2 (Kay SR2).
Changes in version 1.10.9.RELEASE (2017-11-27)
----------------------------------------------
* DATAMONGO-1809 - Type hint usage broken when using positional parameters with more than one digit.
* DATAMONGO-1799 - Release 1.10.9 (Ingalls SR9).
* DATAMONGO-1696 - Reference documentation uses JPA Annotations.
Changes in version 2.0.1.RELEASE (2017-10-27)
---------------------------------------------
* DATAMONGO-1815 - Adapt API changes in Property in test cases.

View File

@@ -1,4 +1,4 @@
Spring Data MongoDB 2.0.1
Spring Data MongoDB 2.0.2
Copyright (c) [2010-2015] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").