Compare commits
35 Commits
issue/reac
...
2.1.3.RELE
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd8bd4f568 | ||
|
|
c75f29dc42 | ||
|
|
e493af7266 | ||
|
|
8d892e5924 | ||
|
|
053299f243 | ||
|
|
872659cc00 | ||
|
|
96978a6194 | ||
|
|
2253d3e301 | ||
|
|
5982ee84f7 | ||
|
|
dd2af6462d | ||
|
|
622643bf24 | ||
|
|
51cc55baac | ||
|
|
0b106e5649 | ||
|
|
8975d93ab3 | ||
|
|
e25b6c49f5 | ||
|
|
7a70c205de | ||
|
|
6045efa450 | ||
|
|
7b0816b3ee | ||
|
|
14e4ea736d | ||
|
|
32e7d9ab7f | ||
|
|
7f35ad9e45 | ||
|
|
60228f6e5a | ||
|
|
7604492b7f | ||
|
|
4680fe0e77 | ||
|
|
b4228c88d3 | ||
|
|
f6ef8c94c8 | ||
|
|
0d0dafa85e | ||
|
|
29aa34619f | ||
|
|
7f19f769c4 | ||
|
|
a40e89d90a | ||
|
|
6b2350200a | ||
|
|
fb50b0f6e7 | ||
|
|
ab568229b5 | ||
|
|
7f9c1bd774 | ||
|
|
670a0978da |
6
pom.xml
6
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.1.0.RELEASE</version>
|
||||
<version>2.1.3.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.1.0.RELEASE</version>
|
||||
<version>2.1.3.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
@@ -27,7 +27,7 @@
|
||||
<properties>
|
||||
<project.type>multi</project.type>
|
||||
<dist.id>spring-data-mongodb</dist.id>
|
||||
<springdata.commons>2.1.0.RELEASE</springdata.commons>
|
||||
<springdata.commons>2.1.3.RELEASE</springdata.commons>
|
||||
<mongo>3.8.2</mongo>
|
||||
<mongo.reactivestreams>1.9.2</mongo.reactivestreams>
|
||||
<jmh.version>1.19</jmh.version>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.1.0.RELEASE</version>
|
||||
<version>2.1.3.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.1.0.RELEASE</version>
|
||||
<version>2.1.3.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>2.1.0.RELEASE</version>
|
||||
<version>2.1.3.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<!-- reactive -->
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.1.0.RELEASE</version>
|
||||
<version>2.1.3.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.1.0.RELEASE</version>
|
||||
<version>2.1.3.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ public class MongoDatabaseUtils {
|
||||
|
||||
ClientSession session = doGetSession(factory, sessionSynchronization);
|
||||
|
||||
if(session == null) {
|
||||
if (session == null) {
|
||||
return StringUtils.hasText(dbName) ? factory.getDb(dbName) : factory.getDb();
|
||||
}
|
||||
|
||||
@@ -118,6 +118,25 @@ public class MongoDatabaseUtils {
|
||||
return StringUtils.hasText(dbName) ? factoryToUse.getDb(dbName) : factoryToUse.getDb();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the {@link MongoDbFactory} is actually bound to a {@link ClientSession} that has an active transaction, or
|
||||
* if a {@link TransactionSynchronization} has been registered for the {@link MongoDbFactory resource} and if the
|
||||
* associated {@link ClientSession} has an {@link ClientSession#hasActiveTransaction() active transaction}.
|
||||
*
|
||||
* @param dbFactory the resource to check transactions for. Must not be {@literal null}.
|
||||
* @return {@literal true} if the factory has an ongoing transaction.
|
||||
* @since 2.1.3
|
||||
*/
|
||||
public static boolean isTransactionActive(MongoDbFactory dbFactory) {
|
||||
|
||||
if (dbFactory.isTransactionActive()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
MongoResourceHolder resourceHolder = (MongoResourceHolder) TransactionSynchronizationManager.getResource(dbFactory);
|
||||
return resourceHolder != null && resourceHolder.hasActiveTransaction();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ClientSession doGetSession(MongoDbFactory dbFactory, SessionSynchronization sessionSynchronization) {
|
||||
|
||||
@@ -140,7 +159,7 @@ public class MongoDatabaseUtils {
|
||||
// init a non native MongoDB transaction by registering a MongoSessionSynchronization
|
||||
|
||||
resourceHolder = new MongoResourceHolder(createClientSession(dbFactory), dbFactory);
|
||||
resourceHolder.getSession().startTransaction();
|
||||
resourceHolder.getRequiredSession().startTransaction();
|
||||
|
||||
TransactionSynchronizationManager
|
||||
.registerSynchronization(new MongoSessionSynchronization(resourceHolder, dbFactory));
|
||||
@@ -187,8 +206,8 @@ public class MongoDatabaseUtils {
|
||||
@Override
|
||||
protected void processResourceAfterCommit(MongoResourceHolder resourceHolder) {
|
||||
|
||||
if (isTransactionActive(resourceHolder)) {
|
||||
resourceHolder.getSession().commitTransaction();
|
||||
if (resourceHolder.hasActiveTransaction()) {
|
||||
resourceHolder.getRequiredSession().commitTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,8 +218,8 @@ public class MongoDatabaseUtils {
|
||||
@Override
|
||||
public void afterCompletion(int status) {
|
||||
|
||||
if (status == TransactionSynchronization.STATUS_ROLLED_BACK && isTransactionActive(this.resourceHolder)) {
|
||||
resourceHolder.getSession().abortTransaction();
|
||||
if (status == TransactionSynchronization.STATUS_ROLLED_BACK && this.resourceHolder.hasActiveTransaction()) {
|
||||
resourceHolder.getRequiredSession().abortTransaction();
|
||||
}
|
||||
|
||||
super.afterCompletion(status);
|
||||
@@ -214,17 +233,8 @@ public class MongoDatabaseUtils {
|
||||
protected void releaseResource(MongoResourceHolder resourceHolder, Object resourceKey) {
|
||||
|
||||
if (resourceHolder.hasActiveSession()) {
|
||||
resourceHolder.getSession().close();
|
||||
resourceHolder.getRequiredSession().close();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isTransactionActive(MongoResourceHolder resourceHolder) {
|
||||
|
||||
if (!resourceHolder.hasSession()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return resourceHolder.getSession().hasActiveTransaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,4 +108,15 @@ public interface MongoDbFactory extends CodecRegistryProvider, MongoSessionProvi
|
||||
* @since 2.1
|
||||
*/
|
||||
MongoDbFactory withSession(ClientSession session);
|
||||
|
||||
/**
|
||||
* Returns if the given {@link MongoDbFactory} is bound to a {@link ClientSession} that has an
|
||||
* {@link ClientSession#hasActiveTransaction() active transaction}.
|
||||
*
|
||||
* @return {@literal true} if there's an active transaction, {@literal false} otherwise.
|
||||
* @since 2.1.3
|
||||
*/
|
||||
default boolean isTransactionActive() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.mongodb.client.ClientSession;
|
||||
* <strong>Note:</strong> Intended for internal usage only.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
* @see MongoTransactionManager
|
||||
* @see org.springframework.data.mongodb.core.MongoTemplate
|
||||
@@ -57,6 +58,22 @@ class MongoResourceHolder extends ResourceHolderSupport {
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the required associated {@link ClientSession}.
|
||||
* @throws IllegalStateException if no {@link ClientSession} is associated with this {@link MongoResourceHolder}.
|
||||
* @since 2.1.3
|
||||
*/
|
||||
ClientSession getRequiredSession() {
|
||||
|
||||
ClientSession session = getSession();
|
||||
|
||||
if (session == null) {
|
||||
throw new IllegalStateException("No session available!");
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the associated {@link MongoDbFactory}.
|
||||
*/
|
||||
@@ -101,7 +118,21 @@ class MongoResourceHolder extends ResourceHolderSupport {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hasServerSession() && !getSession().getServerSession().isClosed();
|
||||
return hasServerSession() && !getRequiredSession().getServerSession().isClosed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if the session has an active transaction.
|
||||
* @since 2.1.3
|
||||
* @see #hasActiveSession()
|
||||
*/
|
||||
boolean hasActiveTransaction() {
|
||||
|
||||
if (!hasActiveSession()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getRequiredSession().hasActiveTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,7 +142,7 @@ class MongoResourceHolder extends ResourceHolderSupport {
|
||||
boolean hasServerSession() {
|
||||
|
||||
try {
|
||||
return getSession().getServerSession() != null;
|
||||
return getRequiredSession().getServerSession() != null;
|
||||
} catch (IllegalStateException serverSessionClosed) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCre
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -75,6 +76,7 @@ import org.w3c.dom.Element;
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Zied Yaich
|
||||
*/
|
||||
public class MappingMongoConverterParser implements BeanDefinitionParser {
|
||||
|
||||
@@ -159,6 +161,7 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BeanDefinition potentiallyCreateValidatingMongoEventListener(Element element, ParserContext parserContext) {
|
||||
|
||||
String disableValidation = element.getAttribute("disable-validation");
|
||||
@@ -180,6 +183,7 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private RuntimeBeanReference getValidator(Object source, ParserContext parserContext) {
|
||||
|
||||
if (!JSR_303_PRESENT) {
|
||||
@@ -197,7 +201,7 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
|
||||
}
|
||||
|
||||
public static String potentiallyCreateMappingContext(Element element, ParserContext parserContext,
|
||||
BeanDefinition conversionsDefinition, String converterId) {
|
||||
@Nullable BeanDefinition conversionsDefinition, @Nullable String converterId) {
|
||||
|
||||
String ctxRef = element.getAttribute("mapping-context-ref");
|
||||
|
||||
@@ -211,7 +215,7 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
|
||||
BeanDefinitionBuilder mappingContextBuilder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(MongoMappingContext.class);
|
||||
|
||||
Set<String> classesToAdd = getInititalEntityClasses(element);
|
||||
Set<String> classesToAdd = getInitialEntityClasses(element);
|
||||
|
||||
if (classesToAdd != null) {
|
||||
mappingContextBuilder.addPropertyValue("initialEntitySet", classesToAdd);
|
||||
@@ -262,6 +266,7 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BeanDefinition getCustomConversions(Element element, ParserContext parserContext) {
|
||||
|
||||
List<Element> customConvertersElements = DomUtils.getChildElementsByTagName(element, "custom-converters");
|
||||
@@ -269,7 +274,7 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
|
||||
if (customConvertersElements.size() == 1) {
|
||||
|
||||
Element customerConvertersElement = customConvertersElements.get(0);
|
||||
ManagedList<BeanMetadataElement> converterBeans = new ManagedList<BeanMetadataElement>();
|
||||
ManagedList<BeanMetadataElement> converterBeans = new ManagedList<>();
|
||||
List<Element> converterElements = DomUtils.getChildElementsByTagName(customerConvertersElement, "converter");
|
||||
|
||||
if (converterElements != null) {
|
||||
@@ -285,9 +290,7 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
|
||||
provider.addExcludeFilter(new NegatingFilter(new AssignableTypeFilter(Converter.class),
|
||||
new AssignableTypeFilter(GenericConverter.class)));
|
||||
|
||||
for (BeanDefinition candidate : provider.findCandidateComponents(packageToScan)) {
|
||||
converterBeans.add(candidate);
|
||||
}
|
||||
converterBeans.addAll(provider.findCandidateComponents(packageToScan));
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder conversionsBuilder = BeanDefinitionBuilder.rootBeanDefinition(MongoCustomConversions.class);
|
||||
@@ -304,7 +307,8 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Set<String> getInititalEntityClasses(Element element) {
|
||||
@Nullable
|
||||
private static Set<String> getInitialEntityClasses(Element element) {
|
||||
|
||||
String basePackage = element.getAttribute(BASE_PACKAGE);
|
||||
|
||||
@@ -317,7 +321,7 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
|
||||
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class));
|
||||
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class));
|
||||
|
||||
Set<String> classes = new ManagedSet<String>();
|
||||
Set<String> classes = new ManagedSet<>();
|
||||
for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
|
||||
classes.add(candidate.getBeanClassName());
|
||||
}
|
||||
@@ -325,6 +329,7 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
|
||||
return classes;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BeanMetadataElement parseConverter(Element element, ParserContext parserContext) {
|
||||
|
||||
String converterRef = element.getAttribute("ref");
|
||||
@@ -375,7 +380,7 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
|
||||
|
||||
Assert.notNull(filters, "TypeFilters must not be null");
|
||||
|
||||
this.delegates = new HashSet<TypeFilter>(Arrays.asList(filters));
|
||||
this.delegates = new HashSet<>(Arrays.asList(filters));
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -83,7 +83,9 @@ public class ChangeStreamEvent<T> {
|
||||
*/
|
||||
@Nullable
|
||||
public Instant getTimestamp() {
|
||||
return raw != null && raw.getClusterTime() != null ? Instant.ofEpochMilli(raw.getClusterTime().getValue()) : null;
|
||||
|
||||
return raw != null && raw.getClusterTime() != null
|
||||
? converter.getConversionService().convert(raw.getClusterTime(), Instant.class) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -242,6 +242,14 @@ class EntityOperations {
|
||||
* @return
|
||||
*/
|
||||
T getBean();
|
||||
|
||||
/**
|
||||
* Returns whether the entity is considered to be new.
|
||||
*
|
||||
* @return
|
||||
* @since 2.1.2
|
||||
*/
|
||||
boolean isNew();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -387,6 +395,15 @@ class EntityOperations {
|
||||
public T getBean() {
|
||||
return map;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.EntityOperations.Entity#isNew()
|
||||
*/
|
||||
@Override
|
||||
public boolean isNew() {
|
||||
return map.get(ID_FIELD) != null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SimpleMappedEntity<T extends Map<String, Object>> extends UnmappedEntity<T> {
|
||||
@@ -549,6 +566,15 @@ class EntityOperations {
|
||||
public T getBean() {
|
||||
return propertyAccessor.getBean();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.EntityOperations.Entity#isNew()
|
||||
*/
|
||||
@Override
|
||||
public boolean isNew() {
|
||||
return entity.isNew(propertyAccessor.getBean());
|
||||
}
|
||||
}
|
||||
|
||||
private static class AdaptibleMappedEntity<T> extends MappedEntity<T> implements AdaptibleEntity<T> {
|
||||
@@ -631,7 +657,9 @@ class EntityOperations {
|
||||
return propertyAccessor.getBean();
|
||||
}
|
||||
|
||||
propertyAccessor.setProperty(entity.getRequiredVersionProperty(), 0);
|
||||
MongoPersistentProperty versionProperty = entity.getRequiredVersionProperty();
|
||||
|
||||
propertyAccessor.setProperty(versionProperty, versionProperty.getType().isPrimitive() ? 1 : 0);
|
||||
|
||||
return propertyAccessor.getBean();
|
||||
}
|
||||
|
||||
@@ -233,6 +233,15 @@ public abstract class MongoDbFactorySupport<C> implements MongoDbFactory {
|
||||
return delegate.withSession(session);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.MongoDbFactory#isTransactionActive()
|
||||
*/
|
||||
@Override
|
||||
public boolean isTransactionActive() {
|
||||
return session != null && session.hasActiveTransaction();
|
||||
}
|
||||
|
||||
private MongoDatabase proxyMongoDatabase(MongoDatabase database) {
|
||||
return createProxyInstance(session, database, MongoDatabase.class);
|
||||
}
|
||||
@@ -241,7 +250,8 @@ public abstract class MongoDbFactorySupport<C> implements MongoDbFactory {
|
||||
return createProxyInstance(session, database, MongoDatabase.class);
|
||||
}
|
||||
|
||||
private MongoCollection<?> proxyCollection(com.mongodb.session.ClientSession session, MongoCollection<?> collection) {
|
||||
private MongoCollection<?> proxyCollection(com.mongodb.session.ClientSession session,
|
||||
MongoCollection<?> collection) {
|
||||
return createProxyInstance(session, collection, MongoCollection.class);
|
||||
}
|
||||
|
||||
|
||||
@@ -1119,7 +1119,16 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
Document document = queryMapper.getMappedObject(query.getQueryObject(),
|
||||
Optional.ofNullable(entityClass).map(it -> mappingContext.getPersistentEntity(entityClass)));
|
||||
|
||||
return execute(collectionName, collection -> collection.count(document, options));
|
||||
return doCount(collectionName, document, options);
|
||||
}
|
||||
|
||||
protected long doCount(String collectionName, Document filter, CountOptions options) {
|
||||
|
||||
if (MongoDatabaseUtils.isTransactionActive(getMongoDbFactory())) {
|
||||
return execute(collectionName, collection -> collection.countDocuments(filter, options));
|
||||
}
|
||||
|
||||
return execute(collectionName, collection -> collection.count(filter, options));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1203,20 +1212,19 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
|
||||
protected <T> T doInsert(String collectionName, T objectToSave, MongoWriter<T> writer) {
|
||||
|
||||
AdaptibleEntity<T> entity = operations.forEntity(objectToSave, mongoConverter.getConversionService());
|
||||
T toSave = entity.initializeVersionProperty();
|
||||
|
||||
BeforeConvertEvent<T> event = new BeforeConvertEvent<>(toSave, collectionName);
|
||||
toSave = maybeEmitEvent(event).getSource();
|
||||
BeforeConvertEvent<T> event = new BeforeConvertEvent<>(objectToSave, collectionName);
|
||||
T toConvert = maybeEmitEvent(event).getSource();
|
||||
|
||||
AdaptibleEntity<T> entity = operations.forEntity(toConvert, mongoConverter.getConversionService());
|
||||
entity.assertUpdateableIdIfNotSet();
|
||||
|
||||
T initialized = entity.initializeVersionProperty();
|
||||
Document dbDoc = entity.toMappedDocument(writer).getDocument();
|
||||
|
||||
maybeEmitEvent(new BeforeSaveEvent<>(toSave, dbDoc, collectionName));
|
||||
Object id = insertDocument(collectionName, dbDoc, toSave.getClass());
|
||||
maybeEmitEvent(new BeforeSaveEvent<>(initialized, dbDoc, collectionName));
|
||||
Object id = insertDocument(collectionName, dbDoc, initialized.getClass());
|
||||
|
||||
T saved = populateIdIfNecessary(toSave, id);
|
||||
T saved = populateIdIfNecessary(initialized, id);
|
||||
maybeEmitEvent(new AfterSaveEvent<>(saved, dbDoc, collectionName));
|
||||
|
||||
return saved;
|
||||
@@ -1348,38 +1356,36 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T doSaveVersioned(AdaptibleEntity<T> source, String collectionName) {
|
||||
|
||||
Number number = source.getVersion();
|
||||
|
||||
if (number != null) {
|
||||
|
||||
// Create query for entity with the id and old version
|
||||
Query query = source.getQueryForVersion();
|
||||
|
||||
// Bump version number
|
||||
T toSave = source.incrementVersion();
|
||||
|
||||
toSave = maybeEmitEvent(new BeforeConvertEvent<T>(toSave, collectionName)).getSource();
|
||||
|
||||
source.assertUpdateableIdIfNotSet();
|
||||
|
||||
MappedDocument mapped = source.toMappedDocument(mongoConverter);
|
||||
|
||||
maybeEmitEvent(new BeforeSaveEvent<>(toSave, mapped.getDocument(), collectionName));
|
||||
Update update = mapped.updateWithoutId();
|
||||
|
||||
UpdateResult result = doUpdate(collectionName, query, update, toSave.getClass(), false, false);
|
||||
|
||||
if (result.getModifiedCount() == 0) {
|
||||
throw new OptimisticLockingFailureException(
|
||||
String.format("Cannot save entity %s with version %s to collection %s. Has it been modified meanwhile?",
|
||||
source.getId(), number, collectionName));
|
||||
}
|
||||
maybeEmitEvent(new AfterSaveEvent<>(toSave, mapped.getDocument(), collectionName));
|
||||
|
||||
return toSave;
|
||||
if (source.isNew()) {
|
||||
return (T) doInsert(collectionName, source.getBean(), this.mongoConverter);
|
||||
}
|
||||
|
||||
return (T) doInsert(collectionName, source.getBean(), this.mongoConverter);
|
||||
// Create query for entity with the id and old version
|
||||
Query query = source.getQueryForVersion();
|
||||
|
||||
// Bump version number
|
||||
T toSave = source.incrementVersion();
|
||||
|
||||
toSave = maybeEmitEvent(new BeforeConvertEvent<T>(toSave, collectionName)).getSource();
|
||||
|
||||
source.assertUpdateableIdIfNotSet();
|
||||
|
||||
MappedDocument mapped = source.toMappedDocument(mongoConverter);
|
||||
|
||||
maybeEmitEvent(new BeforeSaveEvent<>(toSave, mapped.getDocument(), collectionName));
|
||||
Update update = mapped.updateWithoutId();
|
||||
|
||||
UpdateResult result = doUpdate(collectionName, query, update, toSave.getClass(), false, false);
|
||||
|
||||
if (result.getModifiedCount() == 0) {
|
||||
|
||||
throw new OptimisticLockingFailureException(
|
||||
String.format("Cannot save entity %s with version %s to collection %s. Has it been modified meanwhile?",
|
||||
source.getId(), source.getVersion(), collectionName));
|
||||
}
|
||||
maybeEmitEvent(new AfterSaveEvent<>(toSave, mapped.getDocument(), collectionName));
|
||||
|
||||
return toSave;
|
||||
}
|
||||
|
||||
protected <T> T doSave(String collectionName, T objectToSave, MongoWriter<T> writer) {
|
||||
@@ -2820,21 +2826,23 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimized {@link CollectionCallback} that takes an already mappend query and a nullable
|
||||
* Optimized {@link CollectionCallback} that takes an already mapped query and a nullable
|
||||
* {@link com.mongodb.client.model.Collation} to execute a count query limited to one element.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
private static class ExistsCallback implements CollectionCallback<Boolean> {
|
||||
private class ExistsCallback implements CollectionCallback<Boolean> {
|
||||
|
||||
private final Document mappedQuery;
|
||||
private final com.mongodb.client.model.Collation collation;
|
||||
|
||||
@Override
|
||||
public Boolean doInCollection(MongoCollection<Document> collection) throws MongoException, DataAccessException {
|
||||
return collection.count(mappedQuery, new CountOptions().limit(1).collation(collation)) > 0;
|
||||
|
||||
return doCount(collection.getNamespace().getCollectionName(), mappedQuery,
|
||||
new CountOptions().limit(1).collation(collation)) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3343,23 +3351,16 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.MongoTemplate#count(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
|
||||
* @see org.springframework.data.mongodb.core.MongoTemplate#doCount(java.lang.String, org.bson.Document, com.mongodb.client.model.CountOptions)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public long count(Query query, @Nullable Class<?> entityClass, String collectionName) {
|
||||
protected long doCount(String collectionName, Document filter, CountOptions options) {
|
||||
|
||||
if (!session.hasActiveTransaction()) {
|
||||
return super.count(query, entityClass, collectionName);
|
||||
return super.doCount(collectionName, filter, options);
|
||||
}
|
||||
|
||||
CountOptions options = new CountOptions();
|
||||
query.getCollation().map(Collation::toMongoCollation).ifPresent(options::collation);
|
||||
|
||||
Document document = delegate.queryMapper.getMappedObject(query.getQueryObject(),
|
||||
Optional.ofNullable(entityClass).map(it -> delegate.mappingContext.getPersistentEntity(entityClass)));
|
||||
|
||||
return execute(collectionName, collection -> collection.countDocuments(document, options));
|
||||
return execute(collectionName, collection -> collection.countDocuments(filter, options));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1915,7 +1915,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
|
||||
publisher = options.getResumeToken().map(BsonValue::asDocument).map(publisher::resumeAfter).orElse(publisher);
|
||||
publisher = options.getCollation().map(Collation::toMongoCollation).map(publisher::collation).orElse(publisher);
|
||||
publisher = options.getResumeTimestamp().map(it -> new BsonTimestamp(it.toEpochMilli()))
|
||||
publisher = options.getResumeTimestamp().map(it -> new BsonTimestamp((int) it.getEpochSecond(), 0))
|
||||
.map(publisher::startAtOperationTime).orElse(publisher);
|
||||
publisher = publisher.fullDocument(options.getFullDocumentLookup().orElse(fullDocument));
|
||||
|
||||
|
||||
@@ -103,8 +103,8 @@ public class GraphLookupOperation implements InheritsFieldsAggregationOperation
|
||||
|
||||
graphLookup.put("startWith", mappedStartWith.size() == 1 ? mappedStartWith.iterator().next() : mappedStartWith);
|
||||
|
||||
graphLookup.put("connectFromField", connectFrom.getName());
|
||||
graphLookup.put("connectToField", connectTo.getName());
|
||||
graphLookup.put("connectFromField", connectFrom.getTarget());
|
||||
graphLookup.put("connectToField", connectTo.getTarget());
|
||||
graphLookup.put("as", as.getName());
|
||||
|
||||
if (maxDepth != null) {
|
||||
@@ -112,7 +112,7 @@ public class GraphLookupOperation implements InheritsFieldsAggregationOperation
|
||||
}
|
||||
|
||||
if (depthField != null) {
|
||||
graphLookup.put("depthField", depthField.getName());
|
||||
graphLookup.put("depthField", depthField.getTarget());
|
||||
}
|
||||
|
||||
if (restrictSearchWithMatch != null) {
|
||||
|
||||
@@ -248,7 +248,6 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
private ParameterValueProvider<MongoPersistentProperty> getParameterProvider(MongoPersistentEntity<?> entity,
|
||||
DocumentAccessor source, SpELExpressionEvaluator evaluator, ObjectPath path) {
|
||||
|
||||
|
||||
AssociationAwareMongoDbPropertyValueProvider provider = new AssociationAwareMongoDbPropertyValueProvider(source,
|
||||
evaluator, path);
|
||||
PersistentEntityParameterValueProvider<MongoPersistentProperty> parameterProvider = new PersistentEntityParameterValueProvider<>(
|
||||
@@ -287,8 +286,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
|
||||
// Make sure id property is set before all other properties
|
||||
|
||||
Object rawId = readAndPopulateIdentifier(accessor, documentAccessor, entity,
|
||||
path, evaluator);
|
||||
Object rawId = readAndPopulateIdentifier(accessor, documentAccessor, entity, path, evaluator);
|
||||
ObjectPath currentPath = path.push(accessor.getBean(), entity, rawId);
|
||||
|
||||
MongoDbPropertyValueProvider valueProvider = new MongoDbPropertyValueProvider(documentAccessor, evaluator,
|
||||
@@ -620,7 +618,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return;
|
||||
}
|
||||
|
||||
MongoPersistentEntity<?> entity = isSubtype(prop.getType(), obj.getClass())
|
||||
MongoPersistentEntity<?> entity = isSubTypeOf(obj.getClass(), prop.getType())
|
||||
? mappingContext.getRequiredPersistentEntity(obj.getClass())
|
||||
: mappingContext.getRequiredPersistentEntity(type);
|
||||
|
||||
@@ -632,10 +630,6 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
accessor.put(prop, document);
|
||||
}
|
||||
|
||||
private boolean isSubtype(Class<?> left, Class<?> right) {
|
||||
return left.isAssignableFrom(right) && !left.equals(right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns given object as {@link Collection}. Will return the {@link Collection} as is if the source is a
|
||||
* {@link Collection} already, will convert an array into a {@link Collection} or simply create a single element
|
||||
@@ -1012,7 +1006,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
Assert.notNull(path, "Object path must not be null!");
|
||||
|
||||
Class<?> collectionType = targetType.getType();
|
||||
collectionType = Collection.class.isAssignableFrom(collectionType) //
|
||||
collectionType = isSubTypeOf(collectionType, Collection.class) //
|
||||
? collectionType //
|
||||
: List.class;
|
||||
|
||||
@@ -1630,6 +1624,17 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given type is a sub type of the given reference, i.e. assignable but not the exact same type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @param reference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private static boolean isSubTypeOf(Class<?> type, Class<?> reference) {
|
||||
return !type.equals(reference) && reference.isAssignableFrom(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marker class used to indicate we have a non root document object here that might be used within an update - so we
|
||||
* need to preserve type hints for potential nested elements but need to remove it on top level.
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Currency;
|
||||
@@ -26,6 +27,7 @@ import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.bson.BsonTimestamp;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.Binary;
|
||||
import org.bson.types.Code;
|
||||
@@ -86,6 +88,7 @@ abstract class MongoConverters {
|
||||
converters.add(LongToAtomicLongConverter.INSTANCE);
|
||||
converters.add(IntegerToAtomicIntegerConverter.INSTANCE);
|
||||
converters.add(BinaryToByteArrayConverter.INSTANCE);
|
||||
converters.add(BsonTimestampToInstantConverter.INSTANCE);
|
||||
|
||||
return converters;
|
||||
}
|
||||
@@ -465,4 +468,22 @@ abstract class MongoConverters {
|
||||
return source.getData();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Converter} implementation converting {@link BsonTimestamp} into {@link Instant}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1.2
|
||||
*/
|
||||
@ReadingConverter
|
||||
enum BsonTimestampToInstantConverter implements Converter<BsonTimestamp, Instant> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Instant convert(BsonTimestamp source) {
|
||||
return Instant.ofEpochSecond(source.getTime(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ class ChangeStreamTask extends CursorReadingTask<ChangeStreamDocument<Document>,
|
||||
.orElseGet(() -> ClassUtils.isAssignable(Document.class, targetType) ? FullDocument.DEFAULT
|
||||
: FullDocument.UPDATE_LOOKUP);
|
||||
|
||||
startAt = changeStreamOptions.getResumeTimestamp().map(Instant::toEpochMilli).map(BsonTimestamp::new)
|
||||
startAt = changeStreamOptions.getResumeTimestamp().map(it -> new BsonTimestamp((int) it.getEpochSecond(), 0))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,9 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import org.bson.BSON;
|
||||
import org.bson.Document;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -37,6 +39,7 @@ import org.springframework.util.StringUtils;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.DBRef;
|
||||
import com.mongodb.util.JSON;
|
||||
import com.mongodb.util.JSONCallback;
|
||||
|
||||
/**
|
||||
* Query to use a plain JSON String to create the {@link Query} to actually execute.
|
||||
@@ -225,7 +228,8 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
|
||||
String transformedInput = transformQueryAndCollectExpressionParametersIntoBindings(input, bindings);
|
||||
String parseableInput = makeParameterReferencesParseable(transformedInput);
|
||||
|
||||
collectParameterReferencesIntoBindings(bindings, JSON.parse(parseableInput));
|
||||
collectParameterReferencesIntoBindings(bindings,
|
||||
JSON.parse(parseableInput, new LenientPatternDecodingCallback()));
|
||||
|
||||
return transformedInput;
|
||||
}
|
||||
@@ -360,6 +364,43 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link JSONCallback} with lenient handling for {@link PatternSyntaxException} falling back to a placeholder
|
||||
* {@link Pattern} for intermediate query document rendering.
|
||||
*/
|
||||
private static class LenientPatternDecodingCallback extends JSONCallback {
|
||||
|
||||
private static final Pattern EMPTY_MARKER = Pattern.compile("__Spring_Data_MongoDB_Bind_Marker__");
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.mongodb.util.JSONCallback#objectDone()
|
||||
*/
|
||||
@Override
|
||||
public Object objectDone() {
|
||||
return exceptionSwallowingStackReducingObjectDone();
|
||||
}
|
||||
|
||||
private Object exceptionSwallowingStackReducingObjectDone/*CauseWeJustNeedTheStructureNotTheActualValue*/() {
|
||||
|
||||
Object value;
|
||||
|
||||
try {
|
||||
return super.objectDone();
|
||||
} catch (PatternSyntaxException e) {
|
||||
value = EMPTY_MARKER;
|
||||
}
|
||||
|
||||
if (!isStackEmpty()) {
|
||||
_put(curName(), value);
|
||||
} else {
|
||||
value = !BSON.hasDecodeHooks() ? value : BSON.applyDecodingHooks(value);
|
||||
setRoot(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic parameter binding with name or position information.
|
||||
*
|
||||
|
||||
@@ -137,7 +137,7 @@ public class SimpleMongoRepository<T, ID> implements MongoRepository<T, ID> {
|
||||
*/
|
||||
@Override
|
||||
public long count() {
|
||||
return mongoOperations.getCollection(entityInformation.getCollectionName()).count();
|
||||
return mongoOperations.count(new Query(), entityInformation.getCollectionName());
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -78,6 +78,39 @@ public class MongoDatabaseUtilsUnitTests {
|
||||
assertFalse(TransactionSynchronizationManager.isActualTransactionActive());
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2130
|
||||
public void isTransactionActiveShouldDetectTxViaFactory() {
|
||||
|
||||
when(dbFactory.isTransactionActive()).thenReturn(true);
|
||||
|
||||
assertThat(MongoDatabaseUtils.isTransactionActive(dbFactory)).isTrue();
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2130
|
||||
public void isTransactionActiveShouldReturnFalseIfNoTxActive() {
|
||||
|
||||
when(dbFactory.isTransactionActive()).thenReturn(false);
|
||||
|
||||
assertThat(MongoDatabaseUtils.isTransactionActive(dbFactory)).isFalse();
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2130
|
||||
public void isTransactionActiveShouldLookupTxForActiveTransactionSynchronizationViaTxManager() {
|
||||
|
||||
when(dbFactory.isTransactionActive()).thenReturn(false);
|
||||
|
||||
MongoTransactionManager txManager = new MongoTransactionManager(dbFactory);
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(txManager);
|
||||
|
||||
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
||||
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
|
||||
assertThat(MongoDatabaseUtils.isTransactionActive(dbFactory)).isTrue();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1920
|
||||
public void shouldNotStartSessionWhenNoTransactionOngoing() {
|
||||
|
||||
|
||||
@@ -15,11 +15,13 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.config;
|
||||
|
||||
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 static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -28,15 +30,18 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.annotation.Version;
|
||||
import org.springframework.data.domain.AuditorAware;
|
||||
import org.springframework.data.mongodb.core.AuditablePerson;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoClient;
|
||||
|
||||
/**
|
||||
@@ -51,6 +56,9 @@ public class AuditingViaJavaConfigRepositoriesTests {
|
||||
|
||||
@Autowired AuditablePersonRepository auditablePersonRepository;
|
||||
@Autowired AuditorAware<AuditablePerson> auditorAware;
|
||||
@Autowired MongoMappingContext context;
|
||||
@Autowired MongoOperations operations;
|
||||
|
||||
AuditablePerson auditor;
|
||||
|
||||
@Configuration
|
||||
@@ -107,6 +115,61 @@ public class AuditingViaJavaConfigRepositoriesTests {
|
||||
new AnnotationConfigApplicationContext(SimpleConfig.class);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2139
|
||||
public void auditingWorksForVersionedEntityWithWrapperVersion() {
|
||||
|
||||
verifyAuditingViaVersionProperty(new VersionedAuditablePerson(), //
|
||||
it -> it.version, //
|
||||
auditablePersonRepository::save, //
|
||||
null, 0L, 1L);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2139
|
||||
public void auditingWorksForVersionedEntityWithSimpleVersion() {
|
||||
|
||||
verifyAuditingViaVersionProperty(new SimpleVersionedAuditablePerson(), //
|
||||
it -> it.version, //
|
||||
auditablePersonRepository::save, //
|
||||
0L, 1L, 2L);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2139
|
||||
public void auditingWorksForVersionedEntityWithWrapperVersionOnTemplate() {
|
||||
|
||||
verifyAuditingViaVersionProperty(new VersionedAuditablePerson(), //
|
||||
it -> it.version, //
|
||||
operations::save, //
|
||||
null, 0L, 1L);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2139
|
||||
public void auditingWorksForVersionedEntityWithSimpleVersionOnTemplate() {
|
||||
|
||||
verifyAuditingViaVersionProperty(new SimpleVersionedAuditablePerson(), //
|
||||
it -> it.version, //
|
||||
operations::save, //
|
||||
0L, 1L, 2L);
|
||||
}
|
||||
|
||||
private <T extends AuditablePerson> void verifyAuditingViaVersionProperty(T instance,
|
||||
Function<T, Object> versionExtractor, Function<T, T> persister, Object... expectedValues) {
|
||||
|
||||
MongoPersistentEntity<?> entity = context.getRequiredPersistentEntity(instance.getClass());
|
||||
|
||||
assertThat(versionExtractor.apply(instance)).isEqualTo(expectedValues[0]);
|
||||
assertThat(entity.isNew(instance)).isTrue();
|
||||
|
||||
instance = auditablePersonRepository.save(instance);
|
||||
|
||||
assertThat(versionExtractor.apply(instance)).isEqualTo(expectedValues[1]);
|
||||
assertThat(entity.isNew(instance)).isFalse();
|
||||
|
||||
instance = auditablePersonRepository.save(instance);
|
||||
|
||||
assertThat(versionExtractor.apply(instance)).isEqualTo(expectedValues[2]);
|
||||
assertThat(entity.isNew(instance)).isFalse();
|
||||
}
|
||||
|
||||
@Repository
|
||||
static interface AuditablePersonRepository extends MongoRepository<AuditablePerson, String> {}
|
||||
|
||||
@@ -128,4 +191,12 @@ public class AuditingViaJavaConfigRepositoriesTests {
|
||||
return "database";
|
||||
}
|
||||
}
|
||||
|
||||
static class VersionedAuditablePerson extends AuditablePerson {
|
||||
@Version Long version;
|
||||
}
|
||||
|
||||
static class SimpleVersionedAuditablePerson extends AuditablePerson {
|
||||
@Version long version;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.mongodb.config;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
@@ -124,7 +125,8 @@ public class ServerAddressPropertyEditorUnitTests {
|
||||
* We can't tell whether the last part of the hostAddress represents a port or not.
|
||||
*/
|
||||
@Test // DATAMONGO-808
|
||||
public void shouldFailToHandleAmbiguousIPv6HostaddressLongWithoutPortAndWithoutBrackets() throws UnknownHostException {
|
||||
public void shouldFailToHandleAmbiguousIPv6HostaddressLongWithoutPortAndWithoutBrackets()
|
||||
throws UnknownHostException {
|
||||
|
||||
expectedException.expect(IllegalArgumentException.class);
|
||||
|
||||
@@ -173,9 +175,9 @@ public class ServerAddressPropertyEditorUnitTests {
|
||||
|
||||
for (String hostname : hostnames) {
|
||||
try {
|
||||
InetAddress.getByName(hostname);
|
||||
InetAddress.getByName(hostname).isReachable(1500);
|
||||
Assert.fail("Supposedly unresolveable hostname '" + hostname + "' can be resolved.");
|
||||
} catch (UnknownHostException expected) {
|
||||
} catch (IOException expected) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static org.mockito.Mockito.any;
|
||||
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
|
||||
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
|
||||
|
||||
import com.mongodb.MongoNamespace;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigInteger;
|
||||
@@ -135,6 +136,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
when(collection.find(any(org.bson.Document.class), any(Class.class))).thenReturn(findIterable);
|
||||
when(collection.mapReduce(any(), any(), eq(Document.class))).thenReturn(mapReduceIterable);
|
||||
when(collection.count(any(Bson.class), any(CountOptions.class))).thenReturn(1L);
|
||||
when(collection.getNamespace()).thenReturn(new MongoNamespace("db.mock-collection"));
|
||||
when(collection.aggregate(any(List.class), any())).thenReturn(aggregateIterable);
|
||||
when(collection.withReadPreference(any())).thenReturn(collection);
|
||||
when(findIterable.projection(any())).thenReturn(findIterable);
|
||||
|
||||
@@ -29,6 +29,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -1355,7 +1356,7 @@ public class ReactiveMongoTemplateTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2012
|
||||
@Test // DATAMONGO-2012, DATAMONGO-2113
|
||||
public void resumesAtTimestampCorrectly() throws InterruptedException {
|
||||
|
||||
Assumptions.assumeThat(ReplicaSet.required().runsAsReplicaSet()).isTrue();
|
||||
@@ -1372,7 +1373,7 @@ public class ReactiveMongoTemplateTests {
|
||||
Person person2 = new Person("Data", 37);
|
||||
Person person3 = new Person("MongoDB", 39);
|
||||
|
||||
StepVerifier.create(template.save(person1)).expectNextCount(1).verifyComplete();
|
||||
StepVerifier.create(template.save(person1).delayElement(Duration.ofSeconds(1))).expectNextCount(1).verifyComplete();
|
||||
StepVerifier.create(template.save(person2)).expectNextCount(1).verifyComplete();
|
||||
|
||||
Thread.sleep(500); // just give it some time to link receive all events
|
||||
|
||||
@@ -15,19 +15,15 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.aggregation;
|
||||
|
||||
import static org.hamcrest.core.Is.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
|
||||
import static org.springframework.data.mongodb.test.util.Assertions.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.mongodb.core.Person;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.util.JSON;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link GraphLookupOperation}.
|
||||
*
|
||||
@@ -54,8 +50,7 @@ public class GraphLookupOperationUnitTests {
|
||||
.as("reportingHierarchy");
|
||||
|
||||
Document document = graphLookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT);
|
||||
assertThat(document,
|
||||
isBsonObject().containing("$graphLookup.depthField", "depth").containing("$graphLookup.maxDepth", 42L));
|
||||
assertThat(document).containsEntry("$graphLookup.depthField", "depth").containsEntry("$graphLookup.maxDepth", 42L);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1551
|
||||
@@ -70,8 +65,7 @@ public class GraphLookupOperationUnitTests {
|
||||
.as("reportingHierarchy");
|
||||
|
||||
Document document = graphLookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT);
|
||||
assertThat(document,
|
||||
isBsonObject().containing("$graphLookup.restrictSearchWithMatch", new Document("key", "value")));
|
||||
assertThat(document).containsEntry("$graphLookup.restrictSearchWithMatch", new Document("key", "value"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1551
|
||||
@@ -86,9 +80,9 @@ public class GraphLookupOperationUnitTests {
|
||||
|
||||
Document document = graphLookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(document,
|
||||
is(Document.parse("{ $graphLookup : { from: \"employees\", startWith: [\"$reportsTo\", \"$boss\"], "
|
||||
+ "connectFromField: \"reportsTo\", connectToField: \"name\", as: \"reportingHierarchy\" } }")));
|
||||
assertThat(document)
|
||||
.isEqualTo(Document.parse("{ $graphLookup : { from: \"employees\", startWith: [\"$reportsTo\", \"$boss\"], "
|
||||
+ "connectFromField: \"reportsTo\", connectToField: \"name\", as: \"reportingHierarchy\" } }"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1551
|
||||
@@ -103,9 +97,8 @@ public class GraphLookupOperationUnitTests {
|
||||
|
||||
Document document = graphLookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(document,
|
||||
is(Document.parse("{ $graphLookup : { from: \"employees\", startWith: [\"$reportsTo\", { $literal: \"$boss\"}], "
|
||||
+ "connectFromField: \"reportsTo\", connectToField: \"name\", as: \"reportingHierarchy\" } }")));
|
||||
assertThat(document).containsEntry("$graphLookup.startWith",
|
||||
Arrays.asList("$reportsTo", new Document("$literal", "$boss")));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class) // DATAMONGO-1551
|
||||
@@ -131,7 +124,39 @@ public class GraphLookupOperationUnitTests {
|
||||
|
||||
Document document = graphLookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(document, is(Document.parse("{ $graphLookup : { from: \"employees\", startWith: { $literal: \"hello\"}, "
|
||||
+ "connectFromField: \"reportsTo\", connectToField: \"name\", as: \"reportingHierarchy\" } }")));
|
||||
assertThat(document).containsEntry("$graphLookup.startWith", new Document("$literal", "hello"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2096
|
||||
public void connectFromShouldUseTargetFieldInsteadOfAlias() {
|
||||
|
||||
AggregationOperation graphLookupOperation = Aggregation.graphLookup("user").startWith("contacts.userId")
|
||||
.connectFrom("contacts.userId").connectTo("_id").depthField("numConnections").as("connections");
|
||||
|
||||
Document document = graphLookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(document).containsEntry("$graphLookup.startWith", "$contacts.userId");
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2096
|
||||
public void connectToShouldUseTargetFieldInsteadOfAlias() {
|
||||
|
||||
AggregationOperation graphLookupOperation = Aggregation.graphLookup("user").startWith("contacts.userId")
|
||||
.connectFrom("userId").connectTo("connectto.field").depthField("numConnections").as("connections");
|
||||
|
||||
Document document = graphLookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(document).containsEntry("$graphLookup.connectToField", "connectto.field");
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2096
|
||||
public void depthFieldShouldUseTargetFieldInsteadOfAlias() {
|
||||
|
||||
AggregationOperation graphLookupOperation = Aggregation.graphLookup("user").startWith("contacts.userId")
|
||||
.connectFrom("contacts.userId").connectTo("_id").depthField("foo.bar").as("connections");
|
||||
|
||||
Document document = graphLookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(document).containsEntry("$graphLookup.depthField", "foo.bar");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.mongodb.core.DocumentTestUtils.*;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -1913,6 +1914,18 @@ public class MappingMongoConverterUnitTests {
|
||||
assertThat(target).doesNotContainKeys("_class");
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2135
|
||||
public void addsEqualObjectsToCollection() {
|
||||
|
||||
org.bson.Document itemDocument = new org.bson.Document("itemKey", "123");
|
||||
org.bson.Document orderDocument = new org.bson.Document("items",
|
||||
Arrays.asList(itemDocument, itemDocument, itemDocument));
|
||||
|
||||
Order order = converter.read(Order.class, orderDocument);
|
||||
|
||||
assertThat(order.items).hasSize(3);
|
||||
}
|
||||
|
||||
static class GenericType<T> {
|
||||
T content;
|
||||
}
|
||||
@@ -2341,4 +2354,15 @@ public class MappingMongoConverterUnitTests {
|
||||
final @Id String id;
|
||||
String value;
|
||||
}
|
||||
|
||||
// DATAMONGO-2135
|
||||
|
||||
@EqualsAndHashCode // equality check by fields
|
||||
static class SomeItem {
|
||||
String itemKey;
|
||||
}
|
||||
|
||||
static class Order {
|
||||
Collection<SomeItem> items = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,18 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Currency;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.assertj.core.data.TemporalUnitLessThanOffset;
|
||||
import org.bson.BsonTimestamp;
|
||||
import org.bson.Document;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.geo.Box;
|
||||
import org.springframework.data.geo.Circle;
|
||||
@@ -32,6 +36,7 @@ import org.springframework.data.geo.Shape;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.AtomicIntegerToIntegerConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.AtomicLongToLongConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.BigDecimalToStringConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.BsonTimestampToInstantConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.CurrencyToStringConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.IntegerToAtomicIntegerConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.LongToAtomicLongConverter;
|
||||
@@ -39,8 +44,6 @@ import org.springframework.data.mongodb.core.convert.MongoConverters.StringToBig
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToCurrencyConverter;
|
||||
import org.springframework.data.mongodb.core.geo.Sphere;
|
||||
|
||||
import org.bson.Document;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MongoConverters}.
|
||||
*
|
||||
@@ -55,10 +58,10 @@ public class MongoConvertersUnitTests {
|
||||
|
||||
BigDecimal bigDecimal = BigDecimal.valueOf(254, 1);
|
||||
String value = BigDecimalToStringConverter.INSTANCE.convert(bigDecimal);
|
||||
assertThat(value, is("25.4"));
|
||||
assertThat(value).isEqualTo("25.4");
|
||||
|
||||
BigDecimal reference = StringToBigDecimalConverter.INSTANCE.convert(value);
|
||||
assertThat(reference, is(bigDecimal));
|
||||
assertThat(reference).isEqualTo(bigDecimal);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-858
|
||||
@@ -69,7 +72,7 @@ public class MongoConvertersUnitTests {
|
||||
Document document = GeoConverters.BoxToDocumentConverter.INSTANCE.convert(box);
|
||||
Shape shape = GeoConverters.DocumentToBoxConverter.INSTANCE.convert(document);
|
||||
|
||||
assertThat(shape, is((org.springframework.data.geo.Shape) box));
|
||||
assertThat(shape).isEqualTo(box);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-858
|
||||
@@ -80,7 +83,7 @@ public class MongoConvertersUnitTests {
|
||||
Document document = GeoConverters.CircleToDocumentConverter.INSTANCE.convert(circle);
|
||||
Shape shape = GeoConverters.DocumentToCircleConverter.INSTANCE.convert(document);
|
||||
|
||||
assertThat(shape, is((org.springframework.data.geo.Shape) circle));
|
||||
assertThat(shape).isEqualTo(circle);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-858
|
||||
@@ -91,7 +94,7 @@ public class MongoConvertersUnitTests {
|
||||
Document document = GeoConverters.PolygonToDocumentConverter.INSTANCE.convert(polygon);
|
||||
Shape shape = GeoConverters.DocumentToPolygonConverter.INSTANCE.convert(document);
|
||||
|
||||
assertThat(shape, is((org.springframework.data.geo.Shape) polygon));
|
||||
assertThat(shape).isEqualTo(polygon);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-858
|
||||
@@ -102,7 +105,7 @@ public class MongoConvertersUnitTests {
|
||||
Document document = GeoConverters.SphereToDocumentConverter.INSTANCE.convert(sphere);
|
||||
org.springframework.data.geo.Shape shape = GeoConverters.DocumentToSphereConverter.INSTANCE.convert(document);
|
||||
|
||||
assertThat(shape, is((org.springframework.data.geo.Shape) sphere));
|
||||
assertThat(shape).isEqualTo(sphere);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-858
|
||||
@@ -113,36 +116,44 @@ public class MongoConvertersUnitTests {
|
||||
Document document = GeoConverters.PointToDocumentConverter.INSTANCE.convert(point);
|
||||
org.springframework.data.geo.Point converted = GeoConverters.DocumentToPointConverter.INSTANCE.convert(document);
|
||||
|
||||
assertThat(converted, is((org.springframework.data.geo.Point) point));
|
||||
assertThat(converted).isEqualTo(point);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1372
|
||||
public void convertsCurrencyToStringCorrectly() {
|
||||
assertThat(CurrencyToStringConverter.INSTANCE.convert(Currency.getInstance("USD")), is("USD"));
|
||||
assertThat(CurrencyToStringConverter.INSTANCE.convert(Currency.getInstance("USD"))).isEqualTo("USD");
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1372
|
||||
public void convertsStringToCurrencyCorrectly() {
|
||||
assertThat(StringToCurrencyConverter.INSTANCE.convert("USD"), is(Currency.getInstance("USD")));
|
||||
assertThat(StringToCurrencyConverter.INSTANCE.convert("USD")).isEqualTo(Currency.getInstance("USD"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1416
|
||||
public void convertsAtomicLongToLongCorrectly() {
|
||||
assertThat(AtomicLongToLongConverter.INSTANCE.convert(new AtomicLong(100L)), is(100L));
|
||||
assertThat(AtomicLongToLongConverter.INSTANCE.convert(new AtomicLong(100L))).isEqualTo(100L);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1416
|
||||
public void convertsAtomicIntegerToIntegerCorrectly() {
|
||||
assertThat(AtomicIntegerToIntegerConverter.INSTANCE.convert(new AtomicInteger(100)), is(100));
|
||||
assertThat(AtomicIntegerToIntegerConverter.INSTANCE.convert(new AtomicInteger(100))).isEqualTo(100);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1416
|
||||
public void convertsLongToAtomicLongCorrectly() {
|
||||
assertThat(LongToAtomicLongConverter.INSTANCE.convert(100L), is(instanceOf(AtomicLong.class)));
|
||||
assertThat(LongToAtomicLongConverter.INSTANCE.convert(100L)).isInstanceOf(AtomicLong.class);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1416
|
||||
public void convertsIntegerToAtomicIntegerCorrectly() {
|
||||
assertThat(IntegerToAtomicIntegerConverter.INSTANCE.convert(100), is(instanceOf(AtomicInteger.class)));
|
||||
assertThat(IntegerToAtomicIntegerConverter.INSTANCE.convert(100)).isInstanceOf(AtomicInteger.class);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2113
|
||||
public void convertsBsonTimestampToInstantCorrectly() {
|
||||
|
||||
assertThat(BsonTimestampToInstantConverter.INSTANCE.convert(new BsonTimestamp(6615900307735969796L)))
|
||||
.isCloseTo(Instant.ofEpochSecond(1540384327), new TemporalUnitLessThanOffset(100, ChronoUnit.MILLIS));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -405,7 +405,7 @@ public class ChangeStreamTests {
|
||||
.append("user_name", "jellyBelly").append("age", 8).append("_class", User.class.getName()));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2012
|
||||
@Test // DATAMONGO-2012, DATAMONGO-2113
|
||||
public void resumeAtTimestampCorrectly() throws InterruptedException {
|
||||
|
||||
CollectingMessageListener<ChangeStreamDocument<Document>, User> messageListener1 = new CollectingMessageListener<>();
|
||||
@@ -415,6 +415,9 @@ public class ChangeStreamTests {
|
||||
awaitSubscription(subscription1);
|
||||
|
||||
template.save(jellyBelly);
|
||||
|
||||
Thread.sleep(1000); // cluster timestamp is in seconds, so we need to wait at least one.
|
||||
|
||||
template.save(sugarSplashy);
|
||||
|
||||
awaitMessages(messageListener1, 12);
|
||||
|
||||
@@ -25,10 +25,11 @@ import java.util.List;
|
||||
* Utilities for testing long running asnyc message retrieval.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class SubscriptionUtils {
|
||||
|
||||
static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(1);
|
||||
static final Duration DEFAULT_TIMEOUT = Duration.ofMillis(1500);
|
||||
|
||||
/**
|
||||
* Wait for {@link Subscription#isActive() to become active} but not longer than {@link #DEFAULT_TIMEOUT}.
|
||||
|
||||
@@ -590,6 +590,19 @@ public class StringBasedMongoQueryUnitTests {
|
||||
assertThat(query.getQueryObject(), is(new Document("arg0", null)));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2119
|
||||
public void spelShouldIgnoreJsonParseErrorsForRegex() {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByPersonLastnameRegex", Person.class);
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter,
|
||||
new Person("Molly", "Chandler"));
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
|
||||
assertThat(query.getQueryObject().toJson(),
|
||||
is(new BasicQuery("{lastname: {$regex: 'Chandler'}}").getQueryObject().toJson()));
|
||||
}
|
||||
|
||||
private StringBasedMongoQuery createQueryForMethod(String name, Class<?>... parameters) {
|
||||
|
||||
try {
|
||||
@@ -697,10 +710,14 @@ public class StringBasedMongoQueryUnitTests {
|
||||
@Query("{ 'arg0' : '?0', 'arg1' : '?1s' }")
|
||||
List<Person> findByWhenQuotedAndSomeStuffAppended(String arg0, String arg1);
|
||||
|
||||
@Query("{ 'lastname' : { '$regex' : '^(?0|John ?1|?1)'} }") // use spel or some regex string this is fucking bad
|
||||
@Query("{ 'lastname' : { '$regex' : '^(?0|John ?1|?1)'} }") // use spel or some regex string this is bad
|
||||
Person findByLastnameRegex(String lastname, String alternative);
|
||||
|
||||
@Query("{ arg0 : ?#{[0]} }")
|
||||
List<Person> findByUsingSpel(Object arg0);
|
||||
|
||||
@Query("{ 'lastname' : { '$regex' : ?#{[0].lastname} } }")
|
||||
Person findByPersonLastnameRegex(Person key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.mongodb.repository.support;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.assertj.core.api.Assumptions.*;
|
||||
import static org.springframework.data.domain.ExampleMatcher.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -28,14 +29,16 @@ import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Example;
|
||||
import org.springframework.data.domain.ExampleMatcher.StringMatcher;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.ExampleMatcher.*;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.mongodb.MongoTransactionManager;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
@@ -44,9 +47,13 @@ import org.springframework.data.mongodb.repository.Person;
|
||||
import org.springframework.data.mongodb.repository.Person.Sex;
|
||||
import org.springframework.data.mongodb.repository.User;
|
||||
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
|
||||
import org.springframework.data.mongodb.test.util.MongoVersion;
|
||||
import org.springframework.data.mongodb.test.util.MongoVersionRule;
|
||||
import org.springframework.data.mongodb.test.util.ReplicaSet;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
/**
|
||||
* @author A. B. M. Kowser
|
||||
@@ -59,6 +66,7 @@ import org.springframework.test.util.ReflectionTestUtils;
|
||||
public class SimpleMongoRepositoryTests {
|
||||
|
||||
@Autowired private MongoTemplate template;
|
||||
public @Rule MongoVersionRule mongoVersion = MongoVersionRule.any();
|
||||
|
||||
private Person oliver, dave, carter, boyd, stefan, leroi, alicia;
|
||||
private List<Person> all;
|
||||
@@ -383,6 +391,54 @@ public class SimpleMongoRepositoryTests {
|
||||
assertThat(repository.findAll()).containsExactlyInAnyOrder(first, second);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2130
|
||||
@MongoVersion(asOf = "4.0")
|
||||
public void countShouldBePossibleInTransaction() {
|
||||
|
||||
assumeThat(ReplicaSet.required().runsAsReplicaSet()).isTrue();
|
||||
|
||||
MongoTransactionManager txmgr = new MongoTransactionManager(template.getMongoDbFactory());
|
||||
TransactionTemplate tt = new TransactionTemplate(txmgr);
|
||||
tt.afterPropertiesSet();
|
||||
|
||||
long countPreTx = repository.count();
|
||||
|
||||
long count = tt.execute(status -> {
|
||||
|
||||
Person sample = new Person();
|
||||
sample.setLastname("Matthews");
|
||||
|
||||
repository.save(sample);
|
||||
|
||||
return repository.count();
|
||||
});
|
||||
|
||||
assertThat(count).isEqualTo(countPreTx + 1);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2130
|
||||
@MongoVersion(asOf = "4.0")
|
||||
public void existsShouldBePossibleInTransaction() {
|
||||
|
||||
assumeThat(ReplicaSet.required().runsAsReplicaSet()).isTrue();
|
||||
|
||||
MongoTransactionManager txmgr = new MongoTransactionManager(template.getMongoDbFactory());
|
||||
TransactionTemplate tt = new TransactionTemplate(txmgr);
|
||||
tt.afterPropertiesSet();
|
||||
|
||||
boolean exists = tt.execute(status -> {
|
||||
|
||||
Person sample = new Person();
|
||||
sample.setLastname("Matthews");
|
||||
|
||||
repository.save(sample);
|
||||
|
||||
return repository.existsById(sample.getId());
|
||||
});
|
||||
|
||||
assertThat(exists).isTrue();
|
||||
}
|
||||
|
||||
private void assertThatAllReferencePersonsWereStoredCorrectly(Map<String, Person> references, List<Person> saved) {
|
||||
|
||||
for (Person person : saved) {
|
||||
|
||||
@@ -34,7 +34,7 @@ Note that the domain type shown in the preceding example has a property named `i
|
||||
====
|
||||
[source]
|
||||
----
|
||||
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
|
||||
public interface PersonRepository extends PagingAndSortingRepository<Person, String> {
|
||||
|
||||
// additional custom query methods go here
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ Note that the entity defined in the preceding example has a property named `id`
|
||||
====
|
||||
[source]
|
||||
----
|
||||
public interface ReactivePersonRepository extends ReactiveSortingRepository<Person, Long> {
|
||||
public interface ReactivePersonRepository extends ReactiveSortingRepository<Person, String> {
|
||||
|
||||
Flux<Person> findByFirstname(String firstname); <1>
|
||||
|
||||
|
||||
@@ -1,6 +1,60 @@
|
||||
Spring Data MongoDB Changelog
|
||||
=============================
|
||||
|
||||
Changes in version 2.1.3.RELEASE (2018-11-27)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-2139 - Auditing broken for entities using optimistic locking via version properties.
|
||||
* DATAMONGO-2135 - Use List instead of Collection when reading data with MappingMongoConverter.
|
||||
* DATAMONGO-2130 - Repository count errors in ClientSession / Transaction.
|
||||
* DATAMONGO-2121 - Release 2.1.3 (Lovelace SR3).
|
||||
* DATAMONGO-2119 - Combining a SpEL expression and a $regex filter triggers a PatternSyntaxException.
|
||||
|
||||
|
||||
Changes in version 2.0.12.RELEASE (2018-11-27)
|
||||
----------------------------------------------
|
||||
* DATAMONGO-2135 - Use List instead of Collection when reading data with MappingMongoConverter.
|
||||
* DATAMONGO-2119 - Combining a SpEL expression and a $regex filter triggers a PatternSyntaxException.
|
||||
* DATAMONGO-2118 - Fix typo in repositories reference documentation.
|
||||
* DATAMONGO-2109 - Release 2.0.12 (Kay SR12).
|
||||
* DATAMONGO-2098 - Typo in MappingMongoConverterParser.
|
||||
|
||||
|
||||
Changes in version 1.10.17.RELEASE (2018-11-27)
|
||||
-----------------------------------------------
|
||||
* DATAMONGO-2135 - Use List instead of Collection when reading data with MappingMongoConverter.
|
||||
* DATAMONGO-2118 - Fix typo in repositories reference documentation.
|
||||
* DATAMONGO-2110 - Release 1.10.17 (Ingalls SR17).
|
||||
|
||||
|
||||
Changes in version 2.1.2.RELEASE (2018-10-29)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-2118 - Fix typo in repositories reference documentation.
|
||||
* DATAMONGO-2113 - ChangeStreamTasks incorrectly converts resumeAt Instants into BsonTimestamp.
|
||||
* DATAMONGO-2107 - Release 2.1.2 (Lovelace SR2).
|
||||
* DATAMONGO-2098 - Typo in MappingMongoConverterParser.
|
||||
|
||||
|
||||
Changes in version 1.10.16.RELEASE (2018-10-15)
|
||||
-----------------------------------------------
|
||||
* DATAMONGO-2096 - Aggregation.graphLookup.**.connectFrom(String) does not handle nested field.
|
||||
* DATAMONGO-2083 - Release 1.10.16 (Ingalls SR16).
|
||||
|
||||
|
||||
Changes in version 2.0.11.RELEASE (2018-10-15)
|
||||
----------------------------------------------
|
||||
* DATAMONGO-2101 - NoSuchMethodException: org.springframework.data.mongodb.core.geo.GeoJsonPoint.<init>().
|
||||
* DATAMONGO-2096 - Aggregation.graphLookup.**.connectFrom(String) does not handle nested field.
|
||||
* DATAMONGO-2087 - Typo in MongoRepository.
|
||||
* DATAMONGO-2086 - Kotlin Fluent API extensions do not allow projections with find queries.
|
||||
* DATAMONGO-2084 - Release 2.0.11 (Kay SR11).
|
||||
|
||||
|
||||
Changes in version 2.1.1.RELEASE (2018-10-15)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-2096 - Aggregation.graphLookup.**.connectFrom(String) does not handle nested field.
|
||||
* DATAMONGO-2094 - Release 2.1.1 (Lovelace SR1).
|
||||
|
||||
|
||||
Changes in version 2.1.0.RELEASE (2018-09-21)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-2091 - Upgrade to MongoDB Java Driver 3.8.2 and Reactive Streams Driver 1.9.2.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Spring Data MongoDB 2.1 GA
|
||||
Spring Data MongoDB 2.1.3
|
||||
Copyright (c) [2010-2015] Pivotal Software, Inc.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
Reference in New Issue
Block a user