From 0bdecefb09d663a80800b5239328aa5d925d6c21 Mon Sep 17 00:00:00 2001 From: mikereiche Date: Tue, 10 Aug 2021 21:30:45 -0700 Subject: [PATCH] Revert "Scopes and collections for repositories (#1149)" This reverts commit 1581712765afd6d656b7bd3c8ea3169e2734303b. --- .../SimpleCouchbaseClientFactory.java | 8 +- .../AbstractCouchbaseConfiguration.java | 1 + .../couchbase/core/CouchbaseTemplate.java | 17 +- .../core/CouchbaseTemplateSupport.java | 5 +- .../core/ExecutableExistsByIdOperation.java | 7 +- .../ExecutableExistsByIdOperationSupport.java | 24 +- ...utableFindByAnalyticsOperationSupport.java | 2 + .../ExecutableFindByIdOperationSupport.java | 4 +- ...ExecutableFindByQueryOperationSupport.java | 11 +- ...eFindFromReplicasByIdOperationSupport.java | 6 +- .../ExecutableInsertByIdOperationSupport.java | 2 + .../core/ExecutableRemoveByIdOperation.java | 7 +- .../ExecutableRemoveByIdOperationSupport.java | 32 +- ...ecutableRemoveByQueryOperationSupport.java | 7 +- ...ExecutableReplaceByIdOperationSupport.java | 2 + .../ExecutableUpsertByIdOperationSupport.java | 2 + .../core/NonReactiveSupportWrapper.java | 4 +- .../core/ReactiveCouchbaseOperations.java | 10 +- .../core/ReactiveCouchbaseTemplate.java | 27 +- .../ReactiveCouchbaseTemplateSupport.java | 13 +- .../core/ReactiveExistsByIdOperation.java | 7 - .../ReactiveExistsByIdOperationSupport.java | 35 +- ...activeFindByAnalyticsOperationSupport.java | 2 + .../ReactiveFindByIdOperationSupport.java | 31 +- .../core/ReactiveFindByQueryOperation.java | 4 +- .../ReactiveFindByQueryOperationSupport.java | 124 ++--- ...eFindFromReplicasByIdOperationSupport.java | 22 +- .../core/ReactiveInsertByIdOperation.java | 3 + .../ReactiveInsertByIdOperationSupport.java | 31 +- .../core/ReactiveRemoveByIdOperation.java | 7 - .../ReactiveRemoveByIdOperationSupport.java | 74 +-- ...ReactiveRemoveByQueryOperationSupport.java | 44 +- .../ReactiveReplaceByIdOperationSupport.java | 33 +- .../core/ReactiveTemplateSupport.java | 18 +- .../ReactiveUpsertByIdOperationSupport.java | 29 +- .../data/couchbase/core/TemplateSupport.java | 14 +- .../convert/MappingCouchbaseConverter.java | 5 - .../BasicCouchbasePersistentEntity.java | 12 +- .../data/couchbase/core/mapping/Document.java | 16 - .../data/couchbase/core/mapping/Expiry.java | 64 --- .../event/AbstractCouchbaseEventListener.java | 26 +- .../mapping/event/AuditingEntityCallback.java | 4 +- .../mapping/event/AuditingEventListener.java | 24 +- .../event/ReactiveAfterDeleteEvent.java | 28 ++ .../event/ReactiveAfterSaveEvent.java} | 21 +- .../event/ReactiveAuditingEntityCallback.java | 9 +- .../event/ReactiveAuditingEventListener.java | 87 ++++ .../event/ReactiveBeforeConvertEvent.java | 28 ++ .../event/ReactiveBeforeDeleteEvent.java | 28 ++ .../event/ReactiveBeforeSaveEvent.java | 30 ++ .../ValidatingCouchbaseEventListener.java | 1 + .../data/couchbase/core/query/Meta.java | 35 +- .../couchbase/core/query/OptionsBuilder.java | 425 ------------------ .../data/couchbase/core/query/Query.java | 36 +- .../couchbase/core/support/PseudoArgs.java | 79 ++-- .../data/couchbase/repository/Collection.java | 46 -- .../repository/CouchbaseRepository.java | 12 +- .../repository/DynamicProxyable.java | 78 ---- .../repository/{Scope.java => Meta.java} | 20 +- .../data/couchbase/repository/Options.java | 117 ----- .../ReactiveCouchbaseRepository.java | 7 +- .../couchbase/repository/ScanConsistency.java | 9 +- .../ReactiveCouchbaseAuditingRegistrar.java | 27 +- .../query/AbstractCouchbaseQuery.java | 39 +- .../query/AbstractCouchbaseQueryBase.java | 16 +- .../query/AbstractReactiveCouchbaseQuery.java | 33 +- .../query/CouchbaseQueryMethod.java | 78 ++-- .../ReactiveStringBasedCouchbaseQuery.java | 2 +- .../query/StringBasedCouchbaseQuery.java | 6 +- .../query/StringBasedN1qlQueryParser.java | 10 +- .../support/CouchbaseRepositoryBase.java | 123 ----- .../support/CouchbaseRepositoryFactory.java | 14 +- .../support/CrudMethodMetadata.java | 4 - .../CrudMethodMetadataPostProcessor.java | 62 +-- .../support/DynamicInvocationHandler.java | 123 ----- .../ReactiveCouchbaseRepositoryFactory.java | 22 +- .../support/SimpleCouchbaseRepository.java | 90 ++-- .../SimpleReactiveCouchbaseRepository.java | 90 ++-- ...hbaseTemplateKeyValueIntegrationTests.java | 6 +- ...mplateQueryCollectionIntegrationTests.java | 51 +-- .../core/CustomTypeKeyIntegrationTests.java | 3 +- ...hbaseTemplateKeyValueIntegrationTests.java | 3 +- .../data/couchbase/domain/Airport.java | 4 +- .../couchbase/domain/AirportRepository.java | 63 +-- .../data/couchbase/domain/Config.java | 7 +- .../couchbase/domain/PersonRepository.java | 3 +- .../domain/ReactiveAirportRepository.java | 10 +- .../domain/ReactiveNaiveAuditorAware.java | 3 +- .../domain/ReactiveUserColRepository.java | 63 --- .../couchbase/domain/UserColRepository.java | 61 --- .../data/couchbase/domain/UserRepository.java | 4 - ...chbaseRepositoryQueryIntegrationTests.java | 172 +------ ...aseRepositoryKeyValueIntegrationTests.java | 3 +- ...chbaseRepositoryQueryIntegrationTests.java | 38 +- ...sitoryQueryCollectionIntegrationTests.java | 211 --------- ...sitoryQueryCollectionIntegrationTests.java | 215 --------- src/test/resources/logback.xml | 2 +- 97 files changed, 961 insertions(+), 2616 deletions(-) delete mode 100644 src/main/java/org/springframework/data/couchbase/core/mapping/Expiry.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveAfterDeleteEvent.java rename src/{test/java/org/springframework/data/couchbase/domain/UserCol.java => main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveAfterSaveEvent.java} (52%) create mode 100644 src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveAuditingEventListener.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveBeforeConvertEvent.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveBeforeDeleteEvent.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveBeforeSaveEvent.java delete mode 100644 src/main/java/org/springframework/data/couchbase/core/query/OptionsBuilder.java delete mode 100644 src/main/java/org/springframework/data/couchbase/repository/Collection.java delete mode 100644 src/main/java/org/springframework/data/couchbase/repository/DynamicProxyable.java rename src/main/java/org/springframework/data/couchbase/repository/{Scope.java => Meta.java} (66%) delete mode 100644 src/main/java/org/springframework/data/couchbase/repository/Options.java delete mode 100644 src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryBase.java delete mode 100644 src/main/java/org/springframework/data/couchbase/repository/support/DynamicInvocationHandler.java delete mode 100644 src/test/java/org/springframework/data/couchbase/domain/ReactiveUserColRepository.java delete mode 100644 src/test/java/org/springframework/data/couchbase/domain/UserColRepository.java delete mode 100644 src/test/java/org/springframework/data/couchbase/repository/query/CouchbaseRepositoryQueryCollectionIntegrationTests.java delete mode 100644 src/test/java/org/springframework/data/couchbase/repository/query/ReactiveCouchbaseRepositoryQueryCollectionIntegrationTests.java diff --git a/src/main/java/org/springframework/data/couchbase/SimpleCouchbaseClientFactory.java b/src/main/java/org/springframework/data/couchbase/SimpleCouchbaseClientFactory.java index b97b57f9..bcb5034b 100644 --- a/src/main/java/org/springframework/data/couchbase/SimpleCouchbaseClientFactory.java +++ b/src/main/java/org/springframework/data/couchbase/SimpleCouchbaseClientFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors + * Copyright 2012-2020 the original author or authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.data.couchbase; import java.util.function.Supplier; @@ -32,9 +33,6 @@ import com.couchbase.client.java.env.ClusterEnvironment; /** * The default implementation of a {@link CouchbaseClientFactory}. - * - * @author Michael Nitschinger - * @author Michael Reiche */ public class SimpleCouchbaseClientFactory implements CouchbaseClientFactory { @@ -76,7 +74,7 @@ public class SimpleCouchbaseClientFactory implements CouchbaseClientFactory { @Override public CouchbaseClientFactory withScope(final String scopeName) { - return new SimpleCouchbaseClientFactory(cluster, bucket.name(), scopeName != null ? scopeName : getScope().name()); + return new SimpleCouchbaseClientFactory(cluster, bucket.name(), scopeName); } @Override diff --git a/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java b/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java index 9d6437da..225a98d0 100644 --- a/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java +++ b/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java @@ -22,6 +22,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; diff --git a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java index 124cab2d..2f1354dc 100644 --- a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java +++ b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java @@ -28,6 +28,7 @@ import org.springframework.data.couchbase.core.index.CouchbasePersistentEntityIn import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty; +import org.springframework.data.couchbase.core.support.PseudoArgs; import org.springframework.data.mapping.context.MappingContext; import org.springframework.lang.Nullable; @@ -106,25 +107,13 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationContex } @Override - @Deprecated public ExecutableRemoveById removeById() { - return removeById(null); + return new ExecutableRemoveByIdOperationSupport(this).removeById(); } @Override - public ExecutableRemoveById removeById(Class domainType) { - return new ExecutableRemoveByIdOperationSupport(this).removeById(domainType); - } - - @Override - @Deprecated public ExecutableExistsById existsById() { - return existsById(null); - } - - @Override - public ExecutableExistsById existsById(Class domainType) { - return new ExecutableExistsByIdOperationSupport(this).existsById(domainType); + return new ExecutableExistsByIdOperationSupport(this).existsById(); } @Override diff --git a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java index bf794b9c..140f4d9c 100644 --- a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplateSupport.java @@ -17,8 +17,6 @@ package org.springframework.data.couchbase.core; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -40,6 +38,9 @@ import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.ConvertingPropertyAccessor; import org.springframework.util.Assert; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Internal encode/decode support for CouchbaseTemplate. * diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableExistsByIdOperation.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableExistsByIdOperation.java index 34468efe..357ba500 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableExistsByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableExistsByIdOperation.java @@ -36,14 +36,8 @@ public interface ExecutableExistsByIdOperation { /** * Checks if the document exists in the bucket. */ - @Deprecated ExecutableExistsById existsById(); - /** - * Checks if the document exists in the bucket. - */ - ExecutableExistsById existsById(Class domainType); - /** * Terminating operations invoking the actual execution. */ @@ -84,6 +78,7 @@ public interface ExecutableExistsByIdOperation { } /** + * * Fluent method to specify the collection. * * @param the entity type to use for the results. diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableExistsByIdOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableExistsByIdOperationSupport.java index 51942a14..5484d9ab 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableExistsByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableExistsByIdOperationSupport.java @@ -32,34 +32,26 @@ public class ExecutableExistsByIdOperationSupport implements ExecutableExistsByI } @Override - @Deprecated public ExecutableExistsById existsById() { - return existsById(null); - } - - @Override - public ExecutableExistsById existsById(Class domainType) { - return new ExecutableExistsByIdSupport(template, domainType, null, null, null); + return new ExecutableExistsByIdSupport(template, null, null, null); } static class ExecutableExistsByIdSupport implements ExecutableExistsById { private final CouchbaseTemplate template; - private final Class domainType; private final String scope; private final String collection; private final ExistsOptions options; private final ReactiveExistsByIdSupport reactiveSupport; - ExecutableExistsByIdSupport(final CouchbaseTemplate template, final Class domainType, final String scope, - final String collection, final ExistsOptions options) { + ExecutableExistsByIdSupport(final CouchbaseTemplate template, final String scope, final String collection, + final ExistsOptions options) { this.template = template; - this.domainType = domainType; this.scope = scope; this.collection = collection; this.options = options; - this.reactiveSupport = new ReactiveExistsByIdSupport(template.reactive(), domainType, scope, collection, options); + this.reactiveSupport = new ReactiveExistsByIdSupport(template.reactive(), scope, collection, options); } @Override @@ -74,18 +66,20 @@ public class ExecutableExistsByIdOperationSupport implements ExecutableExistsByI @Override public ExistsByIdWithOptions inCollection(final String collection) { - return new ExecutableExistsByIdSupport(template, domainType, scope, collection, options); + Assert.hasText(collection, "Collection must not be null nor empty."); + return new ExecutableExistsByIdSupport(template, scope, collection, options); } @Override public TerminatingExistsById withOptions(final ExistsOptions options) { Assert.notNull(options, "Options must not be null."); - return new ExecutableExistsByIdSupport(template, domainType, scope, collection, options); + return new ExecutableExistsByIdSupport(template, scope, collection, options); } @Override public ExistsByIdInCollection inScope(final String scope) { - return new ExecutableExistsByIdSupport(template, domainType, scope, collection, options); + Assert.hasText(scope, "Scope must not be null nor empty."); + return new ExecutableExistsByIdSupport(template, scope, collection, options); } } diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableFindByAnalyticsOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableFindByAnalyticsOperationSupport.java index ad88bc4d..54924a80 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableFindByAnalyticsOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableFindByAnalyticsOperationSupport.java @@ -97,12 +97,14 @@ public class ExecutableFindByAnalyticsOperationSupport implements ExecutableFind @Override public FindByAnalyticsInCollection inScope(final String scope) { + Assert.hasText(scope, "Scope must not be null nor empty."); return new ExecutableFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope, collection, options); } @Override public FindByAnalyticsWithConsistency inCollection(final String collection) { + Assert.hasText(collection, "Collection must not be null nor empty."); return new ExecutableFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope, collection, options); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableFindByIdOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableFindByIdOperationSupport.java index 087c0cd3..b1d07be5 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableFindByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableFindByIdOperationSupport.java @@ -77,17 +77,19 @@ public class ExecutableFindByIdOperationSupport implements ExecutableFindByIdOpe @Override public FindByIdWithOptions inCollection(final String collection) { + Assert.hasText(collection, "Collection must not be null nor empty."); return new ExecutableFindByIdSupport<>(template, domainType, scope, collection, options, fields); } @Override public FindByIdInCollection inScope(final String scope) { + Assert.hasText(scope, "Scope must not be null nor empty."); return new ExecutableFindByIdSupport<>(template, domainType, scope, collection, options, fields); } @Override public FindByIdInScope project(String... fields) { - Assert.notEmpty(fields, "Fields must not be null."); + Assert.notEmpty(fields, "Fields must not be null nor empty."); return new ExecutableFindByIdSupport<>(template, domainType, scope, collection, options, Arrays.asList(fields)); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableFindByQueryOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableFindByQueryOperationSupport.java index 48d77f02..86cb5880 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableFindByQueryOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableFindByQueryOperationSupport.java @@ -68,8 +68,7 @@ public class ExecutableFindByQueryOperationSupport implements ExecutableFindByQu this.returnType = returnType; this.query = query; this.reactiveSupport = new ReactiveFindByQuerySupport(template.reactive(), domainType, returnType, query, - scanConsistency, scope, collection, options, distinctFields, - new NonReactiveSupportWrapper(template.support())); + scanConsistency, scope, collection, options, distinctFields, new NonReactiveSupportWrapper(template.support())); this.scanConsistency = scanConsistency; this.scope = scope; this.collection = collection; @@ -127,12 +126,8 @@ public class ExecutableFindByQueryOperationSupport implements ExecutableFindByQu @Override public FindByQueryWithProjection distinct(final String[] distinctFields) { Assert.notNull(distinctFields, "distinctFields must not be null!"); - // Coming from an annotation, this cannot be null. - // But a non-null but empty distinctFields means distinct on all fields - // So to indicate do not use distinct, we use {"-"} from the annotation, and here we change it to null. - String[] dFields = distinctFields.length == 1 && "-".equals(distinctFields[0]) ? null : distinctFields; return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope, - collection, options, dFields); + collection, options, distinctFields); } @Override @@ -159,12 +154,14 @@ public class ExecutableFindByQueryOperationSupport implements ExecutableFindByQu @Override public FindByQueryInCollection inScope(final String scope) { + Assert.hasText(scope, "Scope must not be null nor empty."); return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope, collection, options, distinctFields); } @Override public FindByQueryWithConsistency inCollection(final String collection) { + Assert.hasText(collection, "Collection must not be null nor empty."); return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope, collection, options, distinctFields); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableFindFromReplicasByIdOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableFindFromReplicasByIdOperationSupport.java index c087b920..5ef12b54 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableFindFromReplicasByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableFindFromReplicasByIdOperationSupport.java @@ -18,9 +18,9 @@ package org.springframework.data.couchbase.core; import java.util.Collection; import org.springframework.data.couchbase.core.ReactiveFindFromReplicasByIdOperationSupport.ReactiveFindFromReplicasByIdSupport; -import org.springframework.util.Assert; import com.couchbase.client.java.kv.GetAnyReplicaOptions; +import org.springframework.util.Assert; public class ExecutableFindFromReplicasByIdOperationSupport implements ExecutableFindFromReplicasByIdOperation { @@ -54,7 +54,7 @@ public class ExecutableFindFromReplicasByIdOperationSupport implements Executabl this.options = options; this.returnType = returnType; this.reactiveSupport = new ReactiveFindFromReplicasByIdSupport<>(template.reactive(), domainType, returnType, - scope, collection, options, new NonReactiveSupportWrapper(template.support())); + scope, collection, options, new NonReactiveSupportWrapper(template.support())); } @Override @@ -75,11 +75,13 @@ public class ExecutableFindFromReplicasByIdOperationSupport implements Executabl @Override public FindFromReplicasByIdWithOptions inCollection(final String collection) { + Assert.hasText(collection, "Collection must not be null nor empty."); return new ExecutableFindFromReplicasByIdSupport<>(template, domainType, returnType, scope, collection, options); } @Override public FindFromReplicasByIdInCollection inScope(final String scope) { + Assert.hasText(scope, "Scope must not be null nor empty."); return new ExecutableFindFromReplicasByIdSupport<>(template, domainType, returnType, scope, collection, options); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableInsertByIdOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableInsertByIdOperationSupport.java index 52031af2..908cf459 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableInsertByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableInsertByIdOperationSupport.java @@ -89,12 +89,14 @@ public class ExecutableInsertByIdOperationSupport implements ExecutableInsertByI @Override public InsertByIdInCollection inScope(final String scope) { + Assert.hasText(scope, "Scope must not be null nor empty."); return new ExecutableInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo, durabilityLevel, expiry); } @Override public InsertByIdWithOptions inCollection(final String collection) { + Assert.hasText(collection, "Collection must not be null nor empty."); return new ExecutableInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo, durabilityLevel, expiry); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByIdOperation.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByIdOperation.java index 085b451b..67c5392d 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByIdOperation.java @@ -18,6 +18,7 @@ package org.springframework.data.couchbase.core; import java.util.Collection; import java.util.List; +import org.springframework.data.couchbase.core.query.WithConsistency; import org.springframework.data.couchbase.core.support.InCollection; import org.springframework.data.couchbase.core.support.InScope; import org.springframework.data.couchbase.core.support.OneAndAllId; @@ -38,12 +39,6 @@ public interface ExecutableRemoveByIdOperation { /** * Removes a document. */ - ExecutableRemoveById removeById(Class domainType); - - /** - * Removes a document. - */ - @Deprecated ExecutableRemoveById removeById(); /** diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByIdOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByIdOperationSupport.java index 9f79dad3..a006f1d6 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByIdOperationSupport.java @@ -35,21 +35,14 @@ public class ExecutableRemoveByIdOperationSupport implements ExecutableRemoveByI } @Override - @Deprecated public ExecutableRemoveById removeById() { - return removeById(null); - } - - @Override - public ExecutableRemoveById removeById(Class domainType) { - return new ExecutableRemoveByIdSupport(template, domainType, null, null, null, PersistTo.NONE, ReplicateTo.NONE, + return new ExecutableRemoveByIdSupport(template, null, null, null, PersistTo.NONE, ReplicateTo.NONE, DurabilityLevel.NONE, null); } static class ExecutableRemoveByIdSupport implements ExecutableRemoveById { private final CouchbaseTemplate template; - private final Class domainType; private final String scope; private final String collection; private final RemoveOptions options; @@ -59,19 +52,18 @@ public class ExecutableRemoveByIdOperationSupport implements ExecutableRemoveByI private final Long cas; private final ReactiveRemoveByIdSupport reactiveRemoveByIdSupport; - ExecutableRemoveByIdSupport(final CouchbaseTemplate template, final Class domainType, final String scope, - final String collection, final RemoveOptions options, final PersistTo persistTo, final ReplicateTo replicateTo, + ExecutableRemoveByIdSupport(final CouchbaseTemplate template, final String scope, final String collection, + final RemoveOptions options, final PersistTo persistTo, final ReplicateTo replicateTo, final DurabilityLevel durabilityLevel, Long cas) { this.template = template; - this.domainType = domainType; this.scope = scope; this.collection = collection; this.options = options; this.persistTo = persistTo; this.replicateTo = replicateTo; this.durabilityLevel = durabilityLevel; - this.reactiveRemoveByIdSupport = new ReactiveRemoveByIdSupport(template.reactive(), domainType, scope, collection, - options, persistTo, replicateTo, durabilityLevel, cas); + this.reactiveRemoveByIdSupport = new ReactiveRemoveByIdSupport(template.reactive(), scope, collection, options, + persistTo, replicateTo, durabilityLevel, cas); this.cas = cas; } @@ -87,14 +79,15 @@ public class ExecutableRemoveByIdOperationSupport implements ExecutableRemoveByI @Override public RemoveByIdWithOptions inCollection(final String collection) { - return new ExecutableRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo, + Assert.hasText(collection, "Collection must not be null nor empty."); + return new ExecutableRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo, durabilityLevel, cas); } @Override public RemoveByIdInCollection withDurability(final DurabilityLevel durabilityLevel) { Assert.notNull(durabilityLevel, "Durability Level must not be null."); - return new ExecutableRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo, + return new ExecutableRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo, durabilityLevel, cas); } @@ -102,26 +95,27 @@ public class ExecutableRemoveByIdOperationSupport implements ExecutableRemoveByI public RemoveByIdInCollection withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) { Assert.notNull(persistTo, "PersistTo must not be null."); Assert.notNull(replicateTo, "ReplicateTo must not be null."); - return new ExecutableRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo, + return new ExecutableRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo, durabilityLevel, cas); } @Override public TerminatingRemoveById withOptions(final RemoveOptions options) { Assert.notNull(options, "Options must not be null."); - return new ExecutableRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo, + return new ExecutableRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo, durabilityLevel, cas); } @Override public RemoveByIdInCollection inScope(final String scope) { - return new ExecutableRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo, + Assert.hasText(scope, "Scope must not be null nor empty."); + return new ExecutableRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo, durabilityLevel, cas); } @Override public RemoveByIdWithDurability withCas(Long cas) { - return new ExecutableRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo, + return new ExecutableRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo, durabilityLevel, cas); } } diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByQueryOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByQueryOperationSupport.java index a97c62ba..f8574598 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByQueryOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByQueryOperationSupport.java @@ -19,10 +19,10 @@ import java.util.List; import org.springframework.data.couchbase.core.ReactiveRemoveByQueryOperationSupport.ReactiveRemoveByQuerySupport; import org.springframework.data.couchbase.core.query.Query; -import org.springframework.util.Assert; import com.couchbase.client.java.query.QueryOptions; import com.couchbase.client.java.query.QueryScanConsistency; +import org.springframework.util.Assert; public class ExecutableRemoveByQueryOperationSupport implements ExecutableRemoveByQueryOperation { @@ -36,7 +36,8 @@ public class ExecutableRemoveByQueryOperationSupport implements ExecutableRemove @Override public ExecutableRemoveByQuery removeByQuery(Class domainType) { - return new ExecutableRemoveByQuerySupport<>(template, domainType, ALL_QUERY, null, null, null, null); + return new ExecutableRemoveByQuerySupport<>(template, domainType, ALL_QUERY, null, null, + null, null); } static class ExecutableRemoveByQuerySupport implements ExecutableRemoveByQuery { @@ -89,6 +90,7 @@ public class ExecutableRemoveByQueryOperationSupport implements ExecutableRemove @Override public RemoveByQueryWithConsistency inCollection(final String collection) { + Assert.hasText(collection, "Collection must not be null nor empty."); return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection, options); } @@ -102,6 +104,7 @@ public class ExecutableRemoveByQueryOperationSupport implements ExecutableRemove @Override public RemoveByQueryInCollection inScope(final String scope) { + Assert.hasText(scope, "Scope must not be null nor empty."); return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection, options); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableReplaceByIdOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableReplaceByIdOperationSupport.java index ef47eb94..ba8ab3eb 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableReplaceByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableReplaceByIdOperationSupport.java @@ -82,6 +82,7 @@ public class ExecutableReplaceByIdOperationSupport implements ExecutableReplaceB @Override public ReplaceByIdWithOptions inCollection(final String collection) { + Assert.hasText(collection, "Collection must not be null nor empty."); return new ExecutableReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo, durabilityLevel, expiry); } @@ -117,6 +118,7 @@ public class ExecutableReplaceByIdOperationSupport implements ExecutableReplaceB @Override public ReplaceByIdInCollection inScope(final String scope) { + Assert.hasText(scope, "Scope must not be null nor empty."); return new ExecutableReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo, durabilityLevel, expiry); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableUpsertByIdOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableUpsertByIdOperationSupport.java index 61b7a394..0719082c 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableUpsertByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableUpsertByIdOperationSupport.java @@ -89,12 +89,14 @@ public class ExecutableUpsertByIdOperationSupport implements ExecutableUpsertByI @Override public UpsertByIdInCollection inScope(final String scope) { + Assert.hasText(scope, "Scope must not be null nor empty."); return new ExecutableUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo, durabilityLevel, expiry); } @Override public UpsertByIdWithOptions inCollection(final String collection) { + Assert.hasText(collection, "Collection must not be null nor empty."); return new ExecutableUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo, durabilityLevel, expiry); } diff --git a/src/main/java/org/springframework/data/couchbase/core/NonReactiveSupportWrapper.java b/src/main/java/org/springframework/data/couchbase/core/NonReactiveSupportWrapper.java index 6e64ecb3..6f890a7e 100644 --- a/src/main/java/org/springframework/data/couchbase/core/NonReactiveSupportWrapper.java +++ b/src/main/java/org/springframework/data/couchbase/core/NonReactiveSupportWrapper.java @@ -15,10 +15,10 @@ */ package org.springframework.data.couchbase.core; -import reactor.core.publisher.Mono; - import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; + import org.springframework.data.couchbase.core.mapping.event.CouchbaseMappingEvent; +import reactor.core.publisher.Mono; /** * Wrapper of {@link TemplateSupport} methods to adapt them to {@link ReactiveTemplateSupport}. diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseOperations.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseOperations.java index f3b97d35..83e43bbe 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseOperations.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseOperations.java @@ -13,17 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.data.couchbase.core; import org.springframework.data.couchbase.CouchbaseClientFactory; import org.springframework.data.couchbase.core.convert.CouchbaseConverter; +import org.springframework.data.couchbase.core.support.PseudoArgs; /** * Defines common operations on the Couchbase data source, most commonly implemented by * {@link ReactiveCouchbaseTemplate}. - * - * @author Michael Nitschinger - * @author Michael Reiche */ public interface ReactiveCouchbaseOperations extends ReactiveFluentCouchbaseOperations { @@ -47,4 +46,9 @@ public interface ReactiveCouchbaseOperations extends ReactiveFluentCouchbaseOper */ CouchbaseClientFactory getCouchbaseClientFactory(); + /** + * @@return the pseudoArgs from the ThreadLocal field of the CouchbaseOperations + */ + PseudoArgs getPseudoArgs(); + } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplate.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplate.java index a20e438e..2aedc9df 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplate.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplate.java @@ -43,7 +43,7 @@ public class ReactiveCouchbaseTemplate implements ReactiveCouchbaseOperations, A private final CouchbaseConverter converter; private final PersistenceExceptionTranslator exceptionTranslator; private final ReactiveCouchbaseTemplateSupport templateSupport; - private ThreadLocal> threadLocalArgs = null; + private ThreadLocal> threadLocalArgs = new ThreadLocal<>(); public ReactiveCouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter) { this(clientFactory, converter, new JacksonTranslationService()); @@ -64,12 +64,7 @@ public class ReactiveCouchbaseTemplate implements ReactiveCouchbaseOperations, A @Override public ReactiveExistsById existsById() { - return existsById(null); - } - - @Override - public ReactiveExistsById existsById(Class domainType) { - return new ReactiveExistsByIdOperationSupport(this).existsById(domainType); + return new ReactiveExistsByIdOperationSupport(this).existsById(); } @Override @@ -94,12 +89,7 @@ public class ReactiveCouchbaseTemplate implements ReactiveCouchbaseOperations, A @Override public ReactiveRemoveById removeById() { - return removeById(null); - } - - @Override - public ReactiveRemoveById removeById(Class domainType) { - return new ReactiveRemoveByIdOperationSupport(this).removeById(domainType); + return new ReactiveRemoveByIdOperationSupport(this).removeById(); } @Override @@ -168,18 +158,11 @@ public class ReactiveCouchbaseTemplate implements ReactiveCouchbaseOperations, A } /** - * @return the pseudoArgs from the ThreadLocal field + * {@inheritDoc} */ + @Override public PseudoArgs getPseudoArgs() { return threadLocalArgs == null ? null : threadLocalArgs.get(); } - /** - * set the ThreadLocal field - */ - public void setPseudoArgs(PseudoArgs threadLocalArgs) { - this.threadLocalArgs = new ThreadLocal<>(); - this.threadLocalArgs.set(threadLocalArgs); - } - } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java index 87f22af6..d1239370 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateSupport.java @@ -16,6 +16,8 @@ package org.springframework.data.couchbase.core; +import org.springframework.data.couchbase.core.mapping.event.AfterSaveEvent; +import org.springframework.data.couchbase.core.mapping.event.ReactiveAfterSaveEvent; import reactor.core.publisher.Mono; import org.slf4j.Logger; @@ -28,12 +30,11 @@ import org.springframework.data.couchbase.core.convert.translation.TranslationSe import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty; -import org.springframework.data.couchbase.core.mapping.event.AfterSaveEvent; -import org.springframework.data.couchbase.core.mapping.event.BeforeConvertEvent; -import org.springframework.data.couchbase.core.mapping.event.BeforeSaveEvent; import org.springframework.data.couchbase.core.mapping.event.CouchbaseMappingEvent; import org.springframework.data.couchbase.core.mapping.event.ReactiveAfterConvertCallback; import org.springframework.data.couchbase.core.mapping.event.ReactiveBeforeConvertCallback; +import org.springframework.data.couchbase.core.mapping.event.ReactiveBeforeConvertEvent; +import org.springframework.data.couchbase.core.mapping.event.ReactiveBeforeSaveEvent; import org.springframework.data.couchbase.repository.support.MappingCouchbaseEntityInformation; import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.callback.EntityCallbacks; @@ -67,13 +68,13 @@ class ReactiveCouchbaseTemplateSupport implements ApplicationContextAware, React @Override public Mono encodeEntity(final Object entityToEncode) { - return Mono.just(entityToEncode).doOnNext(entity -> maybeEmitEvent(new BeforeConvertEvent<>(entity))) + return Mono.just(entityToEncode).doOnNext(entity -> maybeEmitEvent(new ReactiveBeforeConvertEvent<>(entity))) .flatMap(entity -> maybeCallBeforeConvert(entity, "")).map(maybeNewEntity -> { final CouchbaseDocument converted = new CouchbaseDocument(); converter.write(maybeNewEntity, converted); return converted; }).flatMap(converted -> maybeCallAfterConvert(entityToEncode, converted, "").thenReturn(converted)) - .doOnNext(converted -> maybeEmitEvent(new BeforeSaveEvent<>(entityToEncode, converted))); + .doOnNext(converted -> maybeEmitEvent(new ReactiveBeforeSaveEvent<>(entityToEncode, converted))); } @Override @@ -111,7 +112,7 @@ class ReactiveCouchbaseTemplateSupport implements ApplicationContextAware, React } else { returnValue = entity; } - maybeEmitEvent(new AfterSaveEvent(returnValue, converted)); + maybeEmitEvent(new ReactiveAfterSaveEvent(returnValue, converted)); return returnValue; }); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveExistsByIdOperation.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveExistsByIdOperation.java index 014cc43e..5415468d 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveExistsByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveExistsByIdOperation.java @@ -26,7 +26,6 @@ import org.springframework.data.couchbase.core.support.OneAndAllExistsReactive; import org.springframework.data.couchbase.core.support.WithExistsOptions; import com.couchbase.client.java.kv.ExistsOptions; - /** * Exists Operations * @@ -38,14 +37,8 @@ public interface ReactiveExistsByIdOperation { /** * Checks if the document exists in the bucket. */ - @Deprecated ReactiveExistsById existsById(); - /** - * Checks if the document exists in the bucket. - */ - ReactiveExistsById existsById(Class domainType); - /** * Terminating operations invoking the actual execution. */ diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveExistsByIdOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveExistsByIdOperationSupport.java index 56b64282..8e03dd57 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveExistsByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveExistsByIdOperationSupport.java @@ -25,7 +25,6 @@ import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.data.couchbase.core.query.OptionsBuilder; import org.springframework.data.couchbase.core.support.PseudoArgs; import org.springframework.util.Assert; @@ -42,28 +41,20 @@ public class ReactiveExistsByIdOperationSupport implements ReactiveExistsByIdOpe } @Override - @Deprecated public ReactiveExistsById existsById() { - return existsById(null); - } - - @Override - public ReactiveExistsById existsById(Class domainType) { - return new ReactiveExistsByIdSupport(template, domainType, null, null, null); + return new ReactiveExistsByIdSupport(template, null, null, null); } static class ReactiveExistsByIdSupport implements ReactiveExistsById { private final ReactiveCouchbaseTemplate template; - private final Class domainType; private final String scope; private final String collection; private final ExistsOptions options; - ReactiveExistsByIdSupport(final ReactiveCouchbaseTemplate template, final Class domainType, final String scope, - final String collection, final ExistsOptions options) { + ReactiveExistsByIdSupport(final ReactiveCouchbaseTemplate template, final String scope, final String collection, + final ExistsOptions options) { this.template = template; - this.domainType = domainType; this.scope = scope; this.collection = collection; this.options = options; @@ -71,12 +62,12 @@ public class ReactiveExistsByIdOperationSupport implements ReactiveExistsByIdOpe @Override public Mono one(final String id) { - PseudoArgs pArgs = new PseudoArgs<>(template, scope, collection, options, domainType); - LOG.trace("existsById {}", pArgs); + PseudoArgs pArgs = new PseudoArgs<>(template, scope, collection, + options != null ? options : ExistsOptions.existsOptions()); + LOG.trace("statement: {} scope: {} collection: {}", "exitsById", pArgs.getScope(), pArgs.getCollection()); return Mono.just(id) .flatMap(docId -> template.getCouchbaseClientFactory().withScope(pArgs.getScope()) - .getCollection(pArgs.getCollection()).reactive().exists(id, buildOptions(pArgs.getOptions())) - .map(ExistsResult::exists)) + .getCollection(pArgs.getCollection()).reactive().exists(id, pArgs.getOptions()).map(ExistsResult::exists)) .onErrorMap(throwable -> { if (throwable instanceof RuntimeException) { return template.potentiallyConvertRuntimeException((RuntimeException) throwable); @@ -86,10 +77,6 @@ public class ReactiveExistsByIdOperationSupport implements ReactiveExistsByIdOpe }); } - private ExistsOptions buildOptions(ExistsOptions options) { - return OptionsBuilder.buildExistsOptions(options); - } - @Override public Mono> all(final Collection ids) { return Flux.fromIterable(ids).flatMap(id -> one(id).map(result -> Tuples.of(id, result))) @@ -98,18 +85,20 @@ public class ReactiveExistsByIdOperationSupport implements ReactiveExistsByIdOpe @Override public ExistsByIdWithOptions inCollection(final String collection) { - return new ReactiveExistsByIdSupport(template, domainType, scope, collection, options); + Assert.hasText(collection, "Collection must not be null nor empty."); + return new ReactiveExistsByIdSupport(template, scope, collection, options); } @Override public TerminatingExistsById withOptions(final ExistsOptions options) { Assert.notNull(options, "Options must not be null."); - return new ReactiveExistsByIdSupport(template, domainType, scope, collection, options); + return new ReactiveExistsByIdSupport(template, scope, collection, options); } @Override public ExistsByIdInCollection inScope(final String scope) { - return new ReactiveExistsByIdSupport(template, domainType, scope, collection, options); + Assert.hasText(scope, "Scope must not be null nor empty."); + return new ReactiveExistsByIdSupport(template, scope, collection, options); } } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByAnalyticsOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByAnalyticsOperationSupport.java index 53cce70d..4f64a6b1 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByAnalyticsOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByAnalyticsOperationSupport.java @@ -167,12 +167,14 @@ public class ReactiveFindByAnalyticsOperationSupport implements ReactiveFindByAn @Override public FindByAnalyticsInCollection inScope(final String scope) { + Assert.hasText(scope, "Scope must not be null nor empty."); return new ReactiveFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope, collection, options, support); } @Override public FindByAnalyticsWithConsistency inCollection(final String collection) { + Assert.hasText(collection, "Collection must not be null nor empty."); return new ReactiveFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope, collection, options, support); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByIdOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByIdOperationSupport.java index 0e722ff5..a9973136 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByIdOperationSupport.java @@ -36,6 +36,7 @@ import com.couchbase.client.java.kv.GetOptions; public class ReactiveFindByIdOperationSupport implements ReactiveFindByIdOperation { private final ReactiveCouchbaseTemplate template; + private static final Logger LOG = LoggerFactory.getLogger(ReactiveFindByIdOperationSupport.class); ReactiveFindByIdOperationSupport(ReactiveCouchbaseTemplate template) { @@ -70,19 +71,19 @@ public class ReactiveFindByIdOperationSupport implements ReactiveFindByIdOperati @Override public Mono one(final String id) { - GetOptions gOptions = options != null ? options : getOptions(); - if (gOptions.build().transcoder() == null) { - gOptions.transcoder(RawJsonTranscoder.INSTANCE); - } - if (fields != null && !fields.isEmpty()) { - gOptions.project(fields); - } - PseudoArgs pArgs = new PseudoArgs(template, scope, collection, gOptions, domainType); - LOG.trace("findById {}", pArgs); - return Mono.just(id) - .flatMap(docId -> template.getCouchbaseClientFactory().withScope(pArgs.getScope()) - .getCollection(pArgs.getCollection()).reactive().get(docId, pArgs.getOptions())) - .flatMap(result -> support.decodeEntity(id, result.contentAs(String.class), result.cas(), domainType)) + return Mono.just(id).flatMap(docId -> { + GetOptions gOptions = options != null ? options : getOptions(); + if (gOptions.build().transcoder() == null) { + gOptions.transcoder(RawJsonTranscoder.INSTANCE); + } + if (fields != null && !fields.isEmpty()) { + gOptions.project(fields); + } + PseudoArgs pArgs = new PseudoArgs(template, scope, collection, gOptions); + LOG.trace("statement: {} scope: {} collection: {}", "findById", pArgs.getScope(), pArgs.getCollection()); + return template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getCollection(pArgs.getCollection()) + .reactive().get(docId, pArgs.getOptions()); + }).flatMap(result -> support.decodeEntity(id, result.contentAs(String.class), result.cas(), domainType)) .onErrorResume(throwable -> { if (throwable instanceof RuntimeException) { if (throwable instanceof DocumentNotFoundException) { @@ -112,17 +113,19 @@ public class ReactiveFindByIdOperationSupport implements ReactiveFindByIdOperati @Override public FindByIdWithOptions inCollection(final String collection) { + Assert.hasText(collection, "Collection must not be null nor empty."); return new ReactiveFindByIdSupport<>(template, domainType, scope, collection, options, fields, support); } @Override public FindByIdInCollection inScope(final String scope) { + Assert.hasText(scope, "Scope must not be null nor empty."); return new ReactiveFindByIdSupport<>(template, domainType, scope, collection, options, fields, support); } @Override public FindByIdInScope project(String... fields) { - Assert.notNull(fields, "Fields must not be null"); + Assert.notEmpty(fields, "Fields must not be null nor empty."); return new ReactiveFindByIdSupport<>(template, domainType, scope, collection, options, Arrays.asList(fields), support); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByQueryOperation.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByQueryOperation.java index c7a613b3..de056de0 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByQueryOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByQueryOperation.java @@ -89,6 +89,8 @@ public interface ReactiveFindByQueryOperation { */ Mono exists(); + QueryOptions buildOptions(QueryOptions options); + } /** @@ -168,7 +170,7 @@ public interface ReactiveFindByQueryOperation { } /** - * Fluent method to specify scan consistency. Scan consistency may also come from an annotation. + * Fluent method to specify scan consistency. Scan consistency may also come from an annotation. * * @param the entity type to use for the results. */ diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByQueryOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByQueryOperationSupport.java index d4b88e03..60a50438 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByQueryOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByQueryOperationSupport.java @@ -41,6 +41,7 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO private static final Query ALL_QUERY = new Query(); private final ReactiveCouchbaseTemplate template; + private static final Logger LOG = LoggerFactory.getLogger(ReactiveFindByQueryOperationSupport.class); public ReactiveFindByQueryOperationSupport(final ReactiveCouchbaseTemplate template) { @@ -61,8 +62,11 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO private final Query query; private final QueryScanConsistency scanConsistency; private final String collection; - private final String scope; + private String scope; private final String[] distinctFields; + // this would hold scanConsistency etc. from the fluent api if they were converted from standalone fields + // withScope(scopeName) could put raw("query_context",default:.) + // this is not the options argument in save( entity, options ). That becomes query.getCouchbaseOptions() private final QueryOptions options; private final ReactiveTemplateSupport support; @@ -87,8 +91,7 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO @Override public FindByQueryWithQuery matching(Query query) { QueryScanConsistency scanCons; - if (query.getScanConsistency() != null) { // redundant, since buildQueryOptions() will use - // query.getScanConsistency() + if (query.getScanConsistency() != null) { scanCons = query.getScanConsistency(); } else { scanCons = scanConsistency; @@ -106,12 +109,14 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO @Override public FindByQueryInCollection inScope(final String scope) { + Assert.hasText(scope, "Scope must not be null nor empty."); return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope, collection, options, distinctFields, support); } @Override public FindByQueryWithConsistency inCollection(final String collection) { + Assert.hasText(collection, "Collection must not be null nor empty."); return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope, collection, options, distinctFields, support); } @@ -137,14 +142,10 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO } @Override - public FindByQueryWithDistinct distinct(final String[] distinctFields) { + public FindByQueryWithDistinct distinct(String[] distinctFields) { Assert.notNull(distinctFields, "distinctFields must not be null!"); - // Coming from an annotation, this cannot be null. - // But a non-null but empty distinctFields means distinct on all fields - // So to indicate do not use distinct, we use {"-"} from the annotation, and here we change it to null. - String[] dFields = distinctFields.length == 1 && "-".equals(distinctFields[0]) ? null : distinctFields; return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope, - collection, options, dFields, support); + collection, options, distinctFields, support); } @Override @@ -159,65 +160,72 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO @Override public Flux all() { - PseudoArgs pArgs = new PseudoArgs(template, scope, collection, options, domainType); - String statement = assembleEntityQuery(false, distinctFields, pArgs.getCollection()); - LOG.trace("findByQuery {} statement: {}", pArgs, statement); - Mono allResult = pArgs.getScope() == null - ? template.getCouchbaseClientFactory().getCluster().reactive().query(statement, - buildOptions(pArgs.getOptions())) - : template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getScope().reactive().query(statement, - buildOptions(pArgs.getOptions())); - return Flux.defer(() -> allResult.onErrorMap(throwable -> { - if (throwable instanceof RuntimeException) { - return template.potentiallyConvertRuntimeException((RuntimeException) throwable); - } else { - return throwable; - } - }).flatMapMany(ReactiveQueryResult::rowsAsObject).flatMap(row -> { - String id = ""; - long cas = 0; - if (distinctFields == null) { - if (row.getString(TemplateUtils.SELECT_ID) == null) { - return Flux.error(new CouchbaseException( - "query did not project " + TemplateUtils.SELECT_ID + ". Either use #{#n1ql.selectEntity} or project " - + TemplateUtils.SELECT_ID + " and " + TemplateUtils.SELECT_CAS + " : " + statement)); + return Flux.defer(() -> { + PseudoArgs pArgs = new PseudoArgs(template, scope, collection, options); + String statement = assembleEntityQuery(false, distinctFields, pArgs.getCollection()); + LOG.trace("statement: {} {}", "findByQuery", statement); + Mono allResult = pArgs.getScope() == null + ? template.getCouchbaseClientFactory().getCluster().reactive().query(statement, + buildOptions(pArgs.getOptions())) + : template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getScope().reactive().query(statement, + buildOptions(pArgs.getOptions())); + return allResult.onErrorMap(throwable -> { + if (throwable instanceof RuntimeException) { + return template.potentiallyConvertRuntimeException((RuntimeException) throwable); + } else { + return throwable; } - id = row.getString(TemplateUtils.SELECT_ID); - if (row.getLong(TemplateUtils.SELECT_CAS) == null) { - return Flux.error(new CouchbaseException( - "query did not project " + TemplateUtils.SELECT_CAS + ". Either use #{#n1ql.selectEntity} or project " - + TemplateUtils.SELECT_ID + " and " + TemplateUtils.SELECT_CAS + " : " + statement)); + }).flatMapMany(ReactiveQueryResult::rowsAsObject).flatMap(row -> { + String id = ""; + long cas = 0; + if (distinctFields == null) { + if (row.getString(TemplateUtils.SELECT_ID) == null) { + return Flux.error(new CouchbaseException( + "query did not project " + TemplateUtils.SELECT_ID + ". Either use #{#n1ql.selectEntity} or project " + + TemplateUtils.SELECT_ID + " and " + TemplateUtils.SELECT_CAS + " : " + statement)); + } + id = row.getString(TemplateUtils.SELECT_ID); + if (row.getLong(TemplateUtils.SELECT_CAS) == null) { + return Flux.error(new CouchbaseException( + "query did not project " + TemplateUtils.SELECT_CAS + ". Either use #{#n1ql.selectEntity} or project " + + TemplateUtils.SELECT_ID + " and " + TemplateUtils.SELECT_CAS + " : " + statement)); + } + cas = row.getLong(TemplateUtils.SELECT_CAS); + row.removeKey(TemplateUtils.SELECT_ID); + row.removeKey(TemplateUtils.SELECT_CAS); } - cas = row.getLong(TemplateUtils.SELECT_CAS); - row.removeKey(TemplateUtils.SELECT_ID); - row.removeKey(TemplateUtils.SELECT_CAS); - } - return support.decodeEntity(id, row.toString(), cas, returnType); - })); + return support.decodeEntity(id, row.toString(), cas, returnType); + }); + }); } - private QueryOptions buildOptions(QueryOptions options) { + @Override + public QueryOptions buildOptions(QueryOptions options) { QueryOptions opts = query.buildQueryOptions(options, scanConsistency); return opts; } @Override public Mono count() { - PseudoArgs pArgs = new PseudoArgs(template, scope, collection, options, domainType); - String statement = assembleEntityQuery(true, distinctFields, pArgs.getCollection()); - LOG.trace("findByQuery {} statement: {}", pArgs, statement); - Mono countResult = pArgs.getScope() == null - ? template.getCouchbaseClientFactory().getCluster().reactive().query(statement, - buildOptions(pArgs.getOptions())) - : template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getScope().reactive().query(statement, - buildOptions(pArgs.getOptions())); - return Mono.defer(() -> countResult.onErrorMap(throwable -> { - if (throwable instanceof RuntimeException) { - return template.potentiallyConvertRuntimeException((RuntimeException) throwable); - } else { - return throwable; - } - }).flatMapMany(ReactiveQueryResult::rowsAsObject).map(row -> row.getLong(TemplateUtils.SELECT_COUNT)).next()); + return Mono.defer(() -> { + PseudoArgs pArgs = new PseudoArgs(template, scope, collection, options); + String statement = assembleEntityQuery(true, distinctFields, pArgs.getCollection()); + LOG.trace("statement: {} {}", "findByQuery", statement); + Mono countResult = this.collection == null + ? template.getCouchbaseClientFactory().getCluster().reactive().query(statement, + buildOptions(pArgs.getOptions())) + : template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getScope().reactive().query(statement, + buildOptions(pArgs.getOptions())); + return countResult.onErrorMap(throwable -> { + if (throwable instanceof RuntimeException) { + return template.potentiallyConvertRuntimeException((RuntimeException) throwable); + } else { + return throwable; + } + }).flatMapMany(ReactiveQueryResult::rowsAsObject).map(row -> { + return row.getLong(TemplateUtils.SELECT_COUNT); + }).next(); + }); } @Override diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveFindFromReplicasByIdOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveFindFromReplicasByIdOperationSupport.java index 88cdcd61..d9a8ed60 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveFindFromReplicasByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveFindFromReplicasByIdOperationSupport.java @@ -68,16 +68,16 @@ public class ReactiveFindFromReplicasByIdOperationSupport implements ReactiveFin @Override public Mono any(final String id) { - GetAnyReplicaOptions garOptions = options != null ? options : getAnyReplicaOptions(); - if (garOptions.build().transcoder() == null) { - garOptions.transcoder(RawJsonTranscoder.INSTANCE); - } - PseudoArgs pArgs = new PseudoArgs<>(template, scope, collection, garOptions, domainType); - LOG.trace("getAnyReplica {}", pArgs); - return Mono.just(id) - .flatMap(docId -> template.getCouchbaseClientFactory().withScope(pArgs.getScope()) - .getCollection(pArgs.getCollection()).reactive().getAnyReplica(docId, pArgs.getOptions())) - .flatMap(result -> support.decodeEntity(id, result.contentAs(String.class), result.cas(), returnType)) + return Mono.just(id).flatMap(docId -> { + GetAnyReplicaOptions garOptions = options != null ? options : getAnyReplicaOptions(); + if (garOptions.build().transcoder() == null) { + garOptions.transcoder(RawJsonTranscoder.INSTANCE); + } + PseudoArgs pArgs = new PseudoArgs<>(template, scope, collection, garOptions); + LOG.trace("statement: {} scope: {} collection: {}", "getAnyReplica", pArgs.getScope(), pArgs.getCollection()); + return template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getCollection(pArgs.getCollection()) + .reactive().getAnyReplica(docId, pArgs.getOptions()); + }).flatMap(result -> support.decodeEntity(id, result.contentAs(String.class), result.cas(), returnType)) .onErrorMap(throwable -> { if (throwable instanceof RuntimeException) { return template.potentiallyConvertRuntimeException((RuntimeException) throwable); @@ -101,12 +101,14 @@ public class ReactiveFindFromReplicasByIdOperationSupport implements ReactiveFin @Override public FindFromReplicasByIdWithOptions inCollection(final String collection) { + Assert.hasText(collection, "Collection must not be null nor empty."); return new ReactiveFindFromReplicasByIdSupport<>(template, domainType, returnType, scope, collection, options, support); } @Override public FindFromReplicasByIdInCollection inScope(final String scope) { + Assert.hasText(scope, "Scope must not be null nor empty."); return new ReactiveFindFromReplicasByIdSupport<>(template, domainType, returnType, scope, collection, options, support); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperation.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperation.java index 3caf6047..b7886c1a 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperation.java @@ -21,6 +21,7 @@ import reactor.core.publisher.Mono; import java.time.Duration; import java.util.Collection; +import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; import org.springframework.data.couchbase.core.support.InCollection; import org.springframework.data.couchbase.core.support.InScope; import org.springframework.data.couchbase.core.support.OneAndAllEntityReactive; @@ -67,6 +68,8 @@ public interface ReactiveInsertByIdOperation { @Override Flux all(Collection objects); + InsertOptions buildOptions(InsertOptions options, CouchbaseDocument doc); + } /** diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperationSupport.java index 7514e8ba..53a04794 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperationSupport.java @@ -24,7 +24,6 @@ import java.util.Collection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; -import org.springframework.data.couchbase.core.query.OptionsBuilder; import org.springframework.data.couchbase.core.support.PseudoArgs; import org.springframework.util.Assert; @@ -35,8 +34,8 @@ import com.couchbase.client.java.kv.ReplicateTo; public class ReactiveInsertByIdOperationSupport implements ReactiveInsertByIdOperation { - private final ReactiveCouchbaseTemplate template; private static final Logger LOG = LoggerFactory.getLogger(ReactiveInsertByIdOperationSupport.class); + private final ReactiveCouchbaseTemplate template; public ReactiveInsertByIdOperationSupport(final ReactiveCouchbaseTemplate template) { this.template = template; @@ -79,12 +78,14 @@ public class ReactiveInsertByIdOperationSupport implements ReactiveInsertByIdOpe @Override public Mono one(T object) { - PseudoArgs pArgs = new PseudoArgs(template, scope, collection, options, domainType); - LOG.trace("insertById {}", pArgs); + PseudoArgs pArgs = new PseudoArgs(template, scope, collection, + options != null ? options : InsertOptions.insertOptions()); + LOG.trace("statement: {} scope: {} collection: {} options: {}", "insertById", pArgs.getScope(), + pArgs.getCollection(), pArgs.getOptions()); return Mono.just(object).flatMap(support::encodeEntity) - .flatMap(converted -> template.getCouchbaseClientFactory().withScope(pArgs.getScope()) - .getCollection(pArgs.getCollection()).reactive() - .insert(converted.getId(), converted.export(), buildOptions(pArgs.getOptions(), converted)) + .flatMap(converted -> template.getCouchbaseClientFactory().withScope(pArgs.getScope()) + .getCollection(pArgs.getCollection()).reactive() + .insert(converted.getId(), converted.export(), buildOptions(pArgs.getOptions(), converted)) .flatMap(result -> support.applyUpdatedId(object, converted.getId()) .flatMap(updatedObject -> support.applyUpdatedCas(updatedObject, converted, result.cas())))) .onErrorMap(throwable -> { @@ -101,8 +102,20 @@ public class ReactiveInsertByIdOperationSupport implements ReactiveInsertByIdOpe return Flux.fromIterable(objects).flatMap(this::one); } + @Override public InsertOptions buildOptions(InsertOptions options, CouchbaseDocument doc) { // CouchbaseDocument converted - return OptionsBuilder.buildInsertOptions(options, persistTo, replicateTo, durabilityLevel, expiry, doc); + options = options != null ? options : InsertOptions.insertOptions(); + if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) { + options.durability(persistTo, replicateTo); + } else if (durabilityLevel != DurabilityLevel.NONE) { + options.durability(durabilityLevel); + } + if (expiry != null) { + options.expiry(expiry); + } else if (doc.getExpiration() != 0) { + options.expiry(Duration.ofSeconds(doc.getExpiration())); + } + return options; } @Override @@ -114,12 +127,14 @@ public class ReactiveInsertByIdOperationSupport implements ReactiveInsertByIdOpe @Override public InsertByIdInCollection inScope(final String scope) { + Assert.hasText(scope, "Scope must not be null nor empty."); return new ReactiveInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo, durabilityLevel, expiry, support); } @Override public InsertByIdWithOptions inCollection(final String collection) { + Assert.hasText(collection, "Collection must not be null nor empty."); return new ReactiveInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo, durabilityLevel, expiry, support); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByIdOperation.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByIdOperation.java index eb55622d..300203fb 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByIdOperation.java @@ -40,14 +40,8 @@ public interface ReactiveRemoveByIdOperation { /** * Removes a document. */ - @Deprecated ReactiveRemoveById removeById(); - /** - * Removes a document. - */ - ReactiveRemoveById removeById(Class domainType); - /** * Terminating operations invoking the actual execution. */ @@ -112,7 +106,6 @@ public interface ReactiveRemoveByIdOperation { interface RemoveByIdWithDurability extends RemoveByIdInScope, WithDurability { @Override RemoveByIdInCollection withDurability(DurabilityLevel durabilityLevel); - @Override RemoveByIdInCollection withDurability(PersistTo persistTo, ReplicateTo replicateTo); diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByIdOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByIdOperationSupport.java index b4f64ed7..a337c39e 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByIdOperationSupport.java @@ -15,14 +15,13 @@ */ package org.springframework.data.couchbase.core; +import org.springframework.data.couchbase.core.mapping.event.ReactiveAfterDeleteEvent; +import org.springframework.data.couchbase.core.mapping.event.ReactiveBeforeDeleteEvent; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.Collection; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.couchbase.core.query.OptionsBuilder; import org.springframework.data.couchbase.core.support.PseudoArgs; import org.springframework.util.Assert; @@ -34,28 +33,20 @@ import com.couchbase.client.java.kv.ReplicateTo; public class ReactiveRemoveByIdOperationSupport implements ReactiveRemoveByIdOperation { private final ReactiveCouchbaseTemplate template; - private static final Logger LOG = LoggerFactory.getLogger(ReactiveRemoveByIdOperationSupport.class); public ReactiveRemoveByIdOperationSupport(final ReactiveCouchbaseTemplate template) { this.template = template; } @Override - @Deprecated public ReactiveRemoveById removeById() { - return removeById(null); - } - - @Override - public ReactiveRemoveById removeById(Class domainType) { - return new ReactiveRemoveByIdSupport(template, domainType, null, null, null, PersistTo.NONE, ReplicateTo.NONE, + return new ReactiveRemoveByIdSupport(template, null, null, null, PersistTo.NONE, ReplicateTo.NONE, DurabilityLevel.NONE, null); } static class ReactiveRemoveByIdSupport implements ReactiveRemoveById { private final ReactiveCouchbaseTemplate template; - private final Class domainType; private final String scope; private final String collection; private final RemoveOptions options; @@ -64,11 +55,10 @@ public class ReactiveRemoveByIdOperationSupport implements ReactiveRemoveByIdOpe private final DurabilityLevel durabilityLevel; private final Long cas; - ReactiveRemoveByIdSupport(final ReactiveCouchbaseTemplate template, final Class domainType, final String scope, - final String collection, final RemoveOptions options, final PersistTo persistTo, final ReplicateTo replicateTo, + ReactiveRemoveByIdSupport(final ReactiveCouchbaseTemplate template, final String scope, final String collection, + final RemoveOptions options, final PersistTo persistTo, final ReplicateTo replicateTo, final DurabilityLevel durabilityLevel, Long cas) { this.template = template; - this.domainType = domainType; this.scope = scope; this.collection = collection; this.options = options; @@ -80,19 +70,22 @@ public class ReactiveRemoveByIdOperationSupport implements ReactiveRemoveByIdOpe @Override public Mono one(final String id) { - PseudoArgs pArgs = new PseudoArgs<>(template, scope, collection, options, domainType); - LOG.trace("removeById {}", pArgs); - return Mono.just(id) - .flatMap(docId -> template.getCouchbaseClientFactory().withScope(pArgs.getScope()) - .getCollection(pArgs.getCollection()).reactive().remove(id, buildRemoveOptions(pArgs.getOptions())) - .map(r -> RemoveResult.from(docId, r))) - .onErrorMap(throwable -> { - if (throwable instanceof RuntimeException) { - return template.potentiallyConvertRuntimeException((RuntimeException) throwable); - } else { - return throwable; - } - }); + PseudoArgs pArgs = new PseudoArgs(template, scope, collection, + options != null ? options : RemoveOptions.removeOptions()); + return Mono.just(id).map(r -> { + template.support().maybeEmitEvent(new ReactiveBeforeDeleteEvent<>(r)); + return r; + }).flatMap(docId -> template.getCouchbaseClientFactory().withScope(pArgs.getScope()) + .getCollection(pArgs.getCollection()).reactive().remove(id, buildRemoveOptions(pArgs.getOptions())).map(r -> { + template.support().maybeEmitEvent(new ReactiveAfterDeleteEvent<>(r)); + return RemoveResult.from(docId, r); + })).onErrorMap(throwable -> { + if (throwable instanceof RuntimeException) { + return template.potentiallyConvertRuntimeException((RuntimeException) throwable); + } else { + return throwable; + } + }); } @Override @@ -101,13 +94,22 @@ public class ReactiveRemoveByIdOperationSupport implements ReactiveRemoveByIdOpe } private RemoveOptions buildRemoveOptions(RemoveOptions options) { - return OptionsBuilder.buildRemoveOptions(options, persistTo, replicateTo, durabilityLevel, cas); + options = options != null ? options : RemoveOptions.removeOptions(); + if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) { + options.durability(persistTo, replicateTo); + } else if (durabilityLevel != DurabilityLevel.NONE) { + options.durability(durabilityLevel); + } + if (cas != null) { + options.cas(cas); + } + return options; } @Override public RemoveByIdInCollection withDurability(final DurabilityLevel durabilityLevel) { Assert.notNull(durabilityLevel, "Durability Level must not be null."); - return new ReactiveRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo, + return new ReactiveRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo, durabilityLevel, cas); } @@ -115,32 +117,34 @@ public class ReactiveRemoveByIdOperationSupport implements ReactiveRemoveByIdOpe public RemoveByIdInCollection withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) { Assert.notNull(persistTo, "PersistTo must not be null."); Assert.notNull(replicateTo, "ReplicateTo must not be null."); - return new ReactiveRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo, + return new ReactiveRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo, durabilityLevel, cas); } @Override public RemoveByIdWithDurability inCollection(final String collection) { - return new ReactiveRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo, + Assert.hasText(collection, "Collection must not be null nor empty."); + return new ReactiveRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo, durabilityLevel, cas); } @Override public RemoveByIdInCollection inScope(final String scope) { - return new ReactiveRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo, + Assert.hasText(scope, "Scope must not be null nor empty."); + return new ReactiveRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo, durabilityLevel, cas); } @Override public TerminatingRemoveById withOptions(final RemoveOptions options) { Assert.notNull(options, "Options must not be null."); - return new ReactiveRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo, + return new ReactiveRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo, durabilityLevel, cas); } @Override public RemoveByIdWithDurability withCas(Long cas) { - return new ReactiveRemoveByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo, + return new ReactiveRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo, durabilityLevel, cas); } } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByQueryOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByQueryOperationSupport.java index efcd8658..a41bd7b9 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByQueryOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByQueryOperationSupport.java @@ -15,6 +15,7 @@ */ package org.springframework.data.couchbase.core; +import org.springframework.util.Assert; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -25,7 +26,6 @@ import org.slf4j.LoggerFactory; import org.springframework.data.couchbase.core.query.Query; import org.springframework.data.couchbase.core.support.PseudoArgs; import org.springframework.data.couchbase.core.support.TemplateUtils; -import org.springframework.util.Assert; import com.couchbase.client.java.query.QueryOptions; import com.couchbase.client.java.query.QueryScanConsistency; @@ -36,6 +36,7 @@ public class ReactiveRemoveByQueryOperationSupport implements ReactiveRemoveByQu private static final Query ALL_QUERY = new Query(); private final ReactiveCouchbaseTemplate template; + private static final Logger LOG = LoggerFactory.getLogger(ReactiveRemoveByQueryOperationSupport.class); public ReactiveRemoveByQueryOperationSupport(final ReactiveCouchbaseTemplate template) { @@ -44,7 +45,8 @@ public class ReactiveRemoveByQueryOperationSupport implements ReactiveRemoveByQu @Override public ReactiveRemoveByQuery removeByQuery(Class domainType) { - return new ReactiveRemoveByQuerySupport<>(template, domainType, ALL_QUERY, null, null, null, null); + return new ReactiveRemoveByQuerySupport<>(template, domainType, ALL_QUERY,null, null, + null, null); } static class ReactiveRemoveByQuerySupport implements ReactiveRemoveByQuery { @@ -70,23 +72,25 @@ public class ReactiveRemoveByQueryOperationSupport implements ReactiveRemoveByQu @Override public Flux all() { - PseudoArgs pArgs = new PseudoArgs<>(template, scope, collection, options, domainType); - String statement = assembleDeleteQuery(pArgs.getCollection()); - LOG.trace("removeByQuery {} statement: {}", pArgs, statement); - Mono allResult = pArgs.getScope() == null - ? template.getCouchbaseClientFactory().getCluster().reactive().query(statement, - buildQueryOptions(pArgs.getOptions())) - : template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getScope().reactive().query(statement, - buildQueryOptions(pArgs.getOptions())); - return Flux.defer(() -> allResult.onErrorMap(throwable -> { - if (throwable instanceof RuntimeException) { - return template.potentiallyConvertRuntimeException((RuntimeException) throwable); - } else { - return throwable; - } - }).flatMapMany(ReactiveQueryResult::rowsAsObject) - .map(row -> new RemoveResult(row.getString(TemplateUtils.SELECT_ID), row.getLong(TemplateUtils.SELECT_CAS), - Optional.empty()))); + return Flux.defer(() -> { + PseudoArgs pArgs = new PseudoArgs<>(template, scope, collection, options); + String statement = assembleDeleteQuery(pArgs.getCollection()); + LOG.trace("statement: {}", statement); + Mono allResult = pArgs.getCollection() == null + ? template.getCouchbaseClientFactory().getCluster().reactive().query(statement, + buildQueryOptions(pArgs.getOptions())) + : template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getScope().reactive().query(statement, + buildQueryOptions(pArgs.getOptions())); + return allResult.onErrorMap(throwable -> { + if (throwable instanceof RuntimeException) { + return template.potentiallyConvertRuntimeException((RuntimeException) throwable); + } else { + return throwable; + } + }).flatMapMany(ReactiveQueryResult::rowsAsObject) + .map(row -> new RemoveResult(row.getString(TemplateUtils.SELECT_ID), row.getLong(TemplateUtils.SELECT_CAS), + Optional.empty())); + }); } private QueryOptions buildQueryOptions(QueryOptions options) { @@ -101,6 +105,7 @@ public class ReactiveRemoveByQueryOperationSupport implements ReactiveRemoveByQu @Override public RemoveByQueryWithConsistency inCollection(final String collection) { + Assert.hasText(collection, "Collection must not be null nor empty."); return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection, options); } @@ -131,6 +136,7 @@ public class ReactiveRemoveByQueryOperationSupport implements ReactiveRemoveByQu @Override public RemoveByQueryInCollection inScope(final String scope) { + Assert.hasText(scope, "Scope must not be null nor empty."); return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection, options); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperationSupport.java index 55aebd4d..bca6ecd0 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperationSupport.java @@ -24,7 +24,6 @@ import java.util.Collection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; -import org.springframework.data.couchbase.core.query.OptionsBuilder; import org.springframework.data.couchbase.core.support.PseudoArgs; import org.springframework.util.Assert; @@ -35,8 +34,8 @@ import com.couchbase.client.java.kv.ReplicateTo; public class ReactiveReplaceByIdOperationSupport implements ReactiveReplaceByIdOperation { - private final ReactiveCouchbaseTemplate template; private static final Logger LOG = LoggerFactory.getLogger(ReactiveReplaceByIdOperationSupport.class); + private final ReactiveCouchbaseTemplate template; public ReactiveReplaceByIdOperationSupport(final ReactiveCouchbaseTemplate template) { this.template = template; @@ -79,13 +78,13 @@ public class ReactiveReplaceByIdOperationSupport implements ReactiveReplaceByIdO @Override public Mono one(T object) { - PseudoArgs pArgs = new PseudoArgs<>(template, scope, collection, options, domainType); - LOG.trace("replaceById {}", pArgs); + PseudoArgs pArgs = new PseudoArgs<>(template, scope, collection, + options != null ? options : ReplaceOptions.replaceOptions()); + LOG.trace("statement: {} pArgs: {}", "replaceById", pArgs); return Mono.just(object).flatMap(support::encodeEntity) - .flatMap(converted -> template.getCouchbaseClientFactory().withScope(pArgs.getScope()) - .getCollection(pArgs.getCollection()).reactive() - .replace(converted.getId(), converted.export(), - buildReplaceOptions(pArgs.getOptions(), object, converted)) + .flatMap(converted -> template.getCouchbaseClientFactory().withScope(pArgs.getScope()) + .getCollection(pArgs.getCollection()).reactive() + .replace(converted.getId(), converted.export(), buildReplaceOptions(pArgs.getOptions(), object, converted)) .flatMap(result -> support.applyUpdatedCas(object, converted, result.cas()))) .onErrorMap(throwable -> { if (throwable instanceof RuntimeException) { @@ -102,8 +101,20 @@ public class ReactiveReplaceByIdOperationSupport implements ReactiveReplaceByIdO } private ReplaceOptions buildReplaceOptions(ReplaceOptions options, T object, CouchbaseDocument doc) { - return OptionsBuilder.buildReplaceOptions(options, persistTo, replicateTo, durabilityLevel, expiry, - support.getCas(object), doc); + options = options != null ? options : ReplaceOptions.replaceOptions(); + if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) { + options.durability(persistTo, replicateTo); + } else if (durabilityLevel != DurabilityLevel.NONE) { + options.durability(durabilityLevel); + } + if (expiry != null) { + options.expiry(expiry); + } else if (doc.getExpiration() != 0) { + options.expiry(Duration.ofSeconds(doc.getExpiration())); + } + long cas = support.getCas(object); + options.cas(cas); + return options; } @Override @@ -115,12 +126,14 @@ public class ReactiveReplaceByIdOperationSupport implements ReactiveReplaceByIdO @Override public ReplaceByIdWithDurability inCollection(final String collection) { + Assert.hasText(collection, "Collection must not be null nor empty."); return new ReactiveReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo, durabilityLevel, expiry, support); } @Override public ReplaceByIdInCollection inScope(final String scope) { + Assert.hasText(scope, "Scope must not be null nor empty."); return new ReactiveReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo, durabilityLevel, expiry, support); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveTemplateSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveTemplateSupport.java index 5f897720..38704595 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveTemplateSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveTemplateSupport.java @@ -15,24 +15,24 @@ */ package org.springframework.data.couchbase.core; -import reactor.core.publisher.Mono; - import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; + import org.springframework.data.couchbase.core.mapping.event.CouchbaseMappingEvent; +import reactor.core.publisher.Mono; public interface ReactiveTemplateSupport { - Mono encodeEntity(Object entityToEncode); + Mono encodeEntity(Object entityToEncode); - Mono decodeEntity(String id, String source, long cas, Class entityClass); + Mono decodeEntity(String id, String source, long cas, Class entityClass); - Mono applyUpdatedCas(T entity, CouchbaseDocument converted, long cas); + Mono applyUpdatedCas(T entity, CouchbaseDocument converted, long cas); - Mono applyUpdatedId(T entity, Object id); + Mono applyUpdatedId(T entity, Object id); - Long getCas(Object entity); + Long getCas(Object entity); - String getJavaNameForEntity(Class clazz); + String getJavaNameForEntity(Class clazz); - void maybeEmitEvent(CouchbaseMappingEvent event); + void maybeEmitEvent(CouchbaseMappingEvent event); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperationSupport.java index b2433e10..2c923b5d 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperationSupport.java @@ -21,10 +21,7 @@ import reactor.core.publisher.Mono; import java.time.Duration; import java.util.Collection; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; -import org.springframework.data.couchbase.core.query.OptionsBuilder; import org.springframework.data.couchbase.core.support.PseudoArgs; import org.springframework.util.Assert; @@ -36,7 +33,6 @@ import com.couchbase.client.java.kv.UpsertOptions; public class ReactiveUpsertByIdOperationSupport implements ReactiveUpsertByIdOperation { private final ReactiveCouchbaseTemplate template; - private static final Logger LOG = LoggerFactory.getLogger(ReactiveUpsertByIdOperationSupport.class); public ReactiveUpsertByIdOperationSupport(final ReactiveCouchbaseTemplate template) { this.template = template; @@ -79,12 +75,12 @@ public class ReactiveUpsertByIdOperationSupport implements ReactiveUpsertByIdOpe @Override public Mono one(T object) { - PseudoArgs pArgs = new PseudoArgs(template, scope, collection, options, domainType); - LOG.trace("upsertById {}", pArgs); + PseudoArgs pArgs = new PseudoArgs<>(template, scope, collection, + options != null ? options : UpsertOptions.upsertOptions()); return Mono.just(object).flatMap(support::encodeEntity) - .flatMap(converted -> template.getCouchbaseClientFactory().withScope(pArgs.getScope()) - .getCollection(pArgs.getCollection()).reactive() - .upsert(converted.getId(), converted.export(), buildUpsertOptions(pArgs.getOptions(), converted)) + .flatMap(converted -> template.getCouchbaseClientFactory().withScope(pArgs.getScope()) + .getCollection(pArgs.getCollection()).reactive() + .upsert(converted.getId(), converted.export(), buildUpsertOptions(pArgs.getOptions(), converted)) .flatMap(result -> support.applyUpdatedId(object, converted.getId()) .flatMap(updatedObject -> support.applyUpdatedCas(updatedObject, converted, result.cas())))) .onErrorMap(throwable -> { @@ -102,7 +98,18 @@ public class ReactiveUpsertByIdOperationSupport implements ReactiveUpsertByIdOpe } private UpsertOptions buildUpsertOptions(UpsertOptions options, CouchbaseDocument doc) { - return OptionsBuilder.buildUpsertOptions(options, persistTo, replicateTo, durabilityLevel, expiry, doc); + options = options != null ? options : UpsertOptions.upsertOptions(); + if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) { + options.durability(persistTo, replicateTo); + } else if (durabilityLevel != DurabilityLevel.NONE) { + options.durability(durabilityLevel); + } + if (expiry != null) { + options.expiry(expiry); + } else if (doc.getExpiration() != 0) { + options.expiry(Duration.ofSeconds(doc.getExpiration())); + } + return options; } @Override @@ -114,12 +121,14 @@ public class ReactiveUpsertByIdOperationSupport implements ReactiveUpsertByIdOpe @Override public UpsertByIdWithDurability inCollection(final String collection) { + Assert.hasText(collection, "Collection must not be null nor empty."); return new ReactiveUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo, durabilityLevel, expiry, support); } @Override public UpsertByIdInCollection inScope(final String scope) { + Assert.hasText(scope, "Scope must not be null nor empty."); return new ReactiveUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo, durabilityLevel, expiry, support); } diff --git a/src/main/java/org/springframework/data/couchbase/core/TemplateSupport.java b/src/main/java/org/springframework/data/couchbase/core/TemplateSupport.java index bc42da3f..372543c3 100644 --- a/src/main/java/org/springframework/data/couchbase/core/TemplateSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/TemplateSupport.java @@ -20,17 +20,17 @@ import org.springframework.data.couchbase.core.mapping.event.CouchbaseMappingEve public interface TemplateSupport { - CouchbaseDocument encodeEntity(Object entityToEncode); + CouchbaseDocument encodeEntity(Object entityToEncode); - T decodeEntity(String id, String source, long cas, Class entityClass); + T decodeEntity(String id, String source, long cas, Class entityClass); - T applyUpdatedCas(T entity, CouchbaseDocument converted, long cas); + T applyUpdatedCas(T entity, CouchbaseDocument converted, long cas); - T applyUpdatedId(T entity, Object id); + T applyUpdatedId(T entity, Object id); - long getCas(Object entity); + long getCas(Object entity); - String getJavaNameForEntity(Class clazz); + String getJavaNameForEntity(Class clazz); - void maybeEmitEvent(CouchbaseMappingEvent event); + void maybeEmitEvent(CouchbaseMappingEvent event); } diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java b/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java index 306e8a47..7793be55 100644 --- a/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java +++ b/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java @@ -34,7 +34,6 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.core.CollectionFactory; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; -import org.springframework.data.annotation.Transient; import org.springframework.data.convert.EntityInstantiator; import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; import org.springframework.data.couchbase.core.mapping.CouchbaseList; @@ -533,10 +532,6 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem idAttributes.put(order, convertToString(propertyObj)); } - if (prop.isAnnotationPresent(Transient.class)) { - return; - } - if (!conversions.isSimpleType(propertyObj.getClass())) { writePropertyInternal(propertyObj, target, prop, false); } else { diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntity.java b/src/main/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntity.java index 3aa10ca5..cb82d214 100644 --- a/src/main/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntity.java +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntity.java @@ -21,9 +21,10 @@ import java.util.TimeZone; import java.util.concurrent.TimeUnit; import org.springframework.context.EnvironmentAware; -import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.env.Environment; +import org.springframework.data.annotation.Id; import org.springframework.data.mapping.MappingException; +import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.model.BasicPersistentEntity; import org.springframework.data.util.TypeInformation; import org.springframework.util.Assert; @@ -97,14 +98,11 @@ public class BasicCouchbasePersistentEntity extends BasicPersistentEntity TTL_IN_SECONDS_INCLUSIVE_END) { @@ -123,7 +121,7 @@ public class BasicCouchbasePersistentEntity extends BasicPersistentEntity * SpEL is NOT supported. */ - @AliasFor(annotation = Expiry.class, attribute = "expiryExpression") String expiryExpression() default ""; /** * An optional time unit for the document's {@link #expiry()}, if set. Default is {@link TimeUnit#SECONDS}. */ - @AliasFor(annotation = Expiry.class, attribute = "expiryUnit") TimeUnit expiryUnit() default TimeUnit.SECONDS; /** @@ -79,9 +68,4 @@ public @interface Document { */ boolean touchOnRead() default false; - /** - * An optional string indicating the query scan consistency - */ - @AliasFor(annotation = ScanConsistency.class, attribute = "query") - QueryScanConsistency queryScanConsistency() default QueryScanConsistency.NOT_BOUNDED; } diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/Expiry.java b/src/main/java/org/springframework/data/couchbase/core/mapping/Expiry.java deleted file mode 100644 index 92ca7e0b..00000000 --- a/src/main/java/org/springframework/data/couchbase/core/mapping/Expiry.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2012-2021 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.data.couchbase.core.mapping; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.concurrent.TimeUnit; - -import org.springframework.data.annotation.Persistent; - -/** - * Expiry annotation - * - * @author Michael Nitschinger - */ -@Persistent -@Inherited -@Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE }) -public @interface Expiry { - - /** - * An optional expiry time for the document. Default is no expiry. Only one of two might might be set at the same - * time: either {@link #expiry()} or {@link #expiryExpression()} - */ - int expiry() default 0; - - /** - * Same as {@link #expiry} but allows the actual value to be set using standard Spring property sources mechanism. - * Only one might be set at the same time: either {@link #expiry()} or {@link #expiryExpression()}.
- * Syntax is the same as for {@link org.springframework.core.env.Environment#resolveRequiredPlaceholders(String)}. - *
- *
- * The value will be recalculated for every {@link org.springframework.data.couchbase.core.CouchbaseTemplate} - * save/insert/update call, thus allowing actual expiration to reflect changes on-the-fly as soon as property sources - * change.
- *
- * SpEL is NOT supported. - */ - String expiryExpression() default ""; - - /** - * An optional time unit for the document's {@link #expiry()}, if set. Default is {@link TimeUnit#SECONDS}. - */ - TimeUnit expiryUnit() default TimeUnit.SECONDS; - -} diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/AbstractCouchbaseEventListener.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/AbstractCouchbaseEventListener.java index cb45b252..87f8bcd9 100644 --- a/src/main/java/org/springframework/data/couchbase/core/mapping/event/AbstractCouchbaseEventListener.java +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/AbstractCouchbaseEventListener.java @@ -50,9 +50,13 @@ public class AbstractCouchbaseEventListener implements ApplicationListener implements ApplicationListener, Af */ @Override public Object onBeforeConvert(Object entity, String collection) { - // LOG.trace("onBeforeConvert " + entity); + //LOG.debug("onBeforeConvert " + entity); return entity; // markAudited called in AuditingEventListener.onApplicationEvent() // auditingHandlerFactory.getObject().markAudited(entity); } @@ -65,7 +65,7 @@ public class AuditingEntityCallback implements BeforeConvertCallback, Af */ @Override public Object onAfterConvert(Object entity, CouchbaseDocument document, String collection) { - // LOG.trace("onAfterConvert " + document); + //LOG.debug("onAfterConvert " + document); return entity; } diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/AuditingEventListener.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/AuditingEventListener.java index 1f267636..0ee5aaab 100644 --- a/src/main/java/org/springframework/data/couchbase/core/mapping/event/AuditingEventListener.java +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/AuditingEventListener.java @@ -65,14 +65,24 @@ public class AuditingEventListener implements ApplicationListener auditingHandlerFactory.getObject().markAudited(it)); + // LOG.info(event.getClass().getSimpleName() + " " + event); } - if (event instanceof BeforeSaveEvent) {} - if (event instanceof AfterSaveEvent) {} - if (event instanceof BeforeDeleteEvent) {} - if (event instanceof AfterDeleteEvent) {} - - if (LOG.isTraceEnabled()) { - LOG.trace("{} {}", event.getClass().getSimpleName(), event.getSource()); + if (event instanceof BeforeSaveEvent) { + // LOG.info(event.getClass().getSimpleName() + " " + event); + } + if (event instanceof AfterSaveEvent) { + // LOG.info(event.getClass().getSimpleName() + " " + event); + } + if (event instanceof BeforeDeleteEvent) { + // LOG.info(event.getClass().getSimpleName() + " " + event); + } + if (event instanceof AfterDeleteEvent) { + // LOG.info(event.getClass().getSimpleName() + " " + event); + } + if (!event.getClass().getSimpleName().startsWith("Reactive")) { + if (LOG.isDebugEnabled()) { + LOG.debug(event.getClass().getSimpleName() + " " + event.getSource()); + } } } diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveAfterDeleteEvent.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveAfterDeleteEvent.java new file mode 100644 index 00000000..52c79894 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveAfterDeleteEvent.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012-2020 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.couchbase.core.mapping.event; + +/** + * @author Michael Reiche + */ +public class ReactiveAfterDeleteEvent extends CouchbaseMappingEvent { + + public ReactiveAfterDeleteEvent(E source) { + super(source, null); + } + +} diff --git a/src/test/java/org/springframework/data/couchbase/domain/UserCol.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveAfterSaveEvent.java similarity index 52% rename from src/test/java/org/springframework/data/couchbase/domain/UserCol.java rename to src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveAfterSaveEvent.java index 1c38d569..3c0a97a3 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/UserCol.java +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveAfterSaveEvent.java @@ -14,28 +14,17 @@ * limitations under the License. */ -package org.springframework.data.couchbase.domain; +package org.springframework.data.couchbase.core.mapping.event; -import org.springframework.data.annotation.PersistenceConstructor; -import org.springframework.data.couchbase.core.mapping.Document; -import org.springframework.data.couchbase.repository.Collection; -import org.springframework.data.couchbase.repository.Scope; +import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; /** - * User entity for tests - * - * @author Michael Nitschinger * @author Michael Reiche */ +public class ReactiveAfterSaveEvent extends CouchbaseMappingEvent { -@Document -@Scope("other_scope") -@Collection("other_collection") -public class UserCol extends User { - - @PersistenceConstructor - public UserCol(final String id, final String firstname, final String lastname) { - super(id, firstname, lastname); + public ReactiveAfterSaveEvent(E source, CouchbaseDocument document) { + super(source, document); } } diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveAuditingEntityCallback.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveAuditingEntityCallback.java index ec7d2895..5f2348da 100644 --- a/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveAuditingEntityCallback.java +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveAuditingEntityCallback.java @@ -63,9 +63,7 @@ public class ReactiveAuditingEntityCallback */ @Override public Publisher onBeforeConvert(final Object entity, final String collection) { - if (LOG.isTraceEnabled()) { - LOG.trace("onBeforeConvert {}", entity.toString()); - } + LOG.debug("onBeforeConvert " + entity.toString()); return this.auditingHandlerFactory.getObject().markAudited(entity); } @@ -78,12 +76,9 @@ public class ReactiveAuditingEntityCallback */ @Override public Publisher onAfterConvert(Object entity, CouchbaseDocument document, String collection) { - if (LOG.isTraceEnabled()) { - LOG.trace("onAfterConvert {}", document.toString()); - } + LOG.debug("onAfterConvert " + document.toString()); return Mono.just(entity); } - /* * (non-Javadoc) * diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveAuditingEventListener.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveAuditingEventListener.java new file mode 100644 index 00000000..7d516dd5 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveAuditingEventListener.java @@ -0,0 +1,87 @@ +/* + * Copyright 2012-2021 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.couchbase.core.mapping.event; + +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.context.ApplicationListener; +import org.springframework.data.auditing.AuditingHandler; +import org.springframework.data.auditing.IsNewAwareAuditingHandler; +import org.springframework.data.auditing.ReactiveIsNewAwareAuditingHandler; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.util.Assert; + +/** + * Reactive Event listener to populate auditing related fields on an entity about to be saved. + * + * @author Michael Reiche + */ +public class ReactiveAuditingEventListener implements ApplicationListener> { + + private final ObjectFactory auditingHandlerFactory; + + public ReactiveAuditingEventListener() { + this.auditingHandlerFactory = null; + } + + private static final Logger LOG = LoggerFactory.getLogger(ReactiveAuditingEventListener.class); + + /** + * Creates a new {@link ReactiveAuditingEventListener} using the given {@link MappingContext} and + * {@link AuditingHandler} provided by the given {@link ObjectFactory}. Registered in CouchbaseAuditingRegistrar + * + * @param auditingHandlerFactory must not be {@literal null}. + */ + public ReactiveAuditingEventListener(ObjectFactory auditingHandlerFactory) { + Assert.notNull(auditingHandlerFactory, "IsNewAwareAuditingHandler must not be null!"); + this.auditingHandlerFactory = auditingHandlerFactory; + } + + /* + * (non-Javadoc) + * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) + */ + @Override + public void onApplicationEvent(CouchbaseMappingEvent event) { + if (event instanceof ReactiveBeforeConvertEvent) { + Optional.ofNullable(event.getSource())// + .ifPresent(it -> auditingHandlerFactory.getObject().markAudited(it)); + // LOG.info(event.getClass().getSimpleName() + " " + event); + } + if (event instanceof ReactiveBeforeSaveEvent) { + // LOG.info(event.getClass().getSimpleName() + " " + event); + } + if (event instanceof ReactiveAfterSaveEvent) { + // LOG.info(event.getClass().getSimpleName() + " " + event); + } + if (event instanceof ReactiveBeforeDeleteEvent) { + // LOG.info(event.getClass().getSimpleName() + " " + event); + } + if (event instanceof ReactiveAfterDeleteEvent) { + // LOG.info(event.getClass().getSimpleName() + " " + event); + } + if (event.getClass().getSimpleName().startsWith("Reactive")) { + if (LOG.isDebugEnabled()) { + LOG.debug(event.getClass().getSimpleName() + " " + event.getSource()); + } + } + } + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveBeforeConvertEvent.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveBeforeConvertEvent.java new file mode 100644 index 00000000..8f3ff6c3 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveBeforeConvertEvent.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012-2021 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.couchbase.core.mapping.event; + +/** + * @author Michael Reiche + */ +public class ReactiveBeforeConvertEvent extends CouchbaseMappingEvent { + + public ReactiveBeforeConvertEvent(E source) { + super(source, null); + } + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveBeforeDeleteEvent.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveBeforeDeleteEvent.java new file mode 100644 index 00000000..161c2a45 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveBeforeDeleteEvent.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012-2020 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.couchbase.core.mapping.event; + +/** + * @author Michael Reiche + */ +public class ReactiveBeforeDeleteEvent extends CouchbaseMappingEvent { + + public ReactiveBeforeDeleteEvent(E source) { + super(source, null); + } + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveBeforeSaveEvent.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveBeforeSaveEvent.java new file mode 100644 index 00000000..b96e32b6 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ReactiveBeforeSaveEvent.java @@ -0,0 +1,30 @@ +/* + * Copyright 2012-2021 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.couchbase.core.mapping.event; + +import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; + +/** + * @author Michael Reiche + */ +public class ReactiveBeforeSaveEvent extends CouchbaseMappingEvent { + + public ReactiveBeforeSaveEvent(E source, CouchbaseDocument document) { + super(source, document); + } + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/ValidatingCouchbaseEventListener.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ValidatingCouchbaseEventListener.java index bdfffa57..7d08f35e 100644 --- a/src/main/java/org/springframework/data/couchbase/core/mapping/event/ValidatingCouchbaseEventListener.java +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ValidatingCouchbaseEventListener.java @@ -60,6 +60,7 @@ public class ValidatingCouchbaseEventListener extends AbstractCouchbaseEventList Set violations = validator.validate(source); if (!violations.isEmpty()) { + LOG.info("During object: {} validation violations found: {}", source, violations); throw new ConstraintViolationException(violations); } diff --git a/src/main/java/org/springframework/data/couchbase/core/query/Meta.java b/src/main/java/org/springframework/data/couchbase/core/query/Meta.java index 783838f3..26d165a0 100644 --- a/src/main/java/org/springframework/data/couchbase/core/query/Meta.java +++ b/src/main/java/org/springframework/data/couchbase/core/query/Meta.java @@ -32,9 +32,8 @@ import org.springframework.util.StringUtils; */ public class Meta { - public enum MetaKey { - SCAN_CONSISTENCY("scan_consistency"), SCOPE("scope"), COLLECTION("collection"), EXPIRY("expiry"), EXPIRY_UNIT( - "expiry_unit"), EXPIRY_EXPRESSION("expiry_expression"), TIMEOUT("timeout"), RETRY_STRATEGY("retry_strategy"); + private enum MetaKey { + EXAMPLE("$example"); private String key; @@ -43,7 +42,7 @@ public class Meta { } } - private final Map values = new LinkedHashMap<>(2); + private final Map values = new LinkedHashMap<>(2); public Meta() {} @@ -69,7 +68,7 @@ public class Meta { * * @return */ - public Iterable> values() { + public Iterable> values() { return Collections.unmodifiableSet(this.values.entrySet()); } @@ -79,26 +78,10 @@ public class Meta { * @param key must not be {@literal null} or empty. * @param value */ - public void setValue(String key, @Nullable Object value) { + void setValue(String key, @Nullable Object value) { Assert.hasText(key, "Meta key must not be 'null' or blank."); - if (value == null || (value instanceof String && !StringUtils.hasText((String) value))) { - this.values.remove(MetaKey.valueOf(key)); - } - this.values.put(MetaKey.valueOf(key), value); - } - - public void setValue(MetaKey key, @Nullable Object value) { - - if (value == null || (value instanceof String && !StringUtils.hasText((String) value))) { - this.values.remove(key); - } - this.values.put(key, value); - } - - public void set(MetaKey key, @Nullable Object value) { - if (value == null || (value instanceof String && !StringUtils.hasText((String) value))) { this.values.remove(key); } @@ -107,15 +90,11 @@ public class Meta { @Nullable @SuppressWarnings("unchecked") - public T getValue(String key) { - return (T) this.values.get(MetaKey.valueOf(key)); - } - - public T get(MetaKey key) { + private T getValue(String key) { return (T) this.values.get(key); } - public T getValue(String key, T defaultValue) { + private T getValue(String key, T defaultValue) { T value = getValue(key); return value != null ? value : defaultValue; diff --git a/src/main/java/org/springframework/data/couchbase/core/query/OptionsBuilder.java b/src/main/java/org/springframework/data/couchbase/core/query/OptionsBuilder.java deleted file mode 100644 index 36cd9d35..00000000 --- a/src/main/java/org/springframework/data/couchbase/core/query/OptionsBuilder.java +++ /dev/null @@ -1,425 +0,0 @@ -/* - * Copyright 2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.couchbase.core.query; - -import static org.springframework.data.couchbase.core.query.Meta.MetaKey.RETRY_STRATEGY; -import static org.springframework.data.couchbase.core.query.Meta.MetaKey.SCAN_CONSISTENCY; -import static org.springframework.data.couchbase.core.query.Meta.MetaKey.TIMEOUT; - -import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.time.Duration; -import java.util.Optional; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.annotation.AnnotatedElementUtils; -import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; -import org.springframework.data.couchbase.repository.Collection; -import org.springframework.data.couchbase.repository.ScanConsistency; -import org.springframework.data.couchbase.repository.Scope; -import org.springframework.data.couchbase.repository.query.CouchbaseQueryMethod; - -import com.couchbase.client.core.io.CollectionIdentifier; -import com.couchbase.client.core.msg.kv.DurabilityLevel; -import com.couchbase.client.core.retry.RetryStrategy; -import com.couchbase.client.java.json.JsonArray; -import com.couchbase.client.java.json.JsonObject; -import com.couchbase.client.java.kv.ExistsOptions; -import com.couchbase.client.java.kv.InsertOptions; -import com.couchbase.client.java.kv.PersistTo; -import com.couchbase.client.java.kv.RemoveOptions; -import com.couchbase.client.java.kv.ReplaceOptions; -import com.couchbase.client.java.kv.ReplicateTo; -import com.couchbase.client.java.kv.UpsertOptions; -import com.couchbase.client.java.query.QueryOptions; -import com.couchbase.client.java.query.QueryScanConsistency; - -public class OptionsBuilder { - - private static final Logger LOG = LoggerFactory.getLogger(OptionsBuilder.class); - - static QueryOptions buildQueryOptions(Query query, QueryOptions options, QueryScanConsistency scanConsistency) { - options = options != null ? options : QueryOptions.queryOptions(); - if (query.getParameters() != null) { - if (query.getParameters() instanceof JsonArray) { - options.parameters((JsonArray) query.getParameters()); - } else { - options.parameters((JsonObject) query.getParameters()); - } - } - - Meta meta = query.getMeta() != null ? query.getMeta() : new Meta(); - QueryOptions.Built optsBuilt = options.build(); - JsonObject optsJson = getQueryOpts(optsBuilt); - QueryScanConsistency metaQueryScanConsistency = meta.get(SCAN_CONSISTENCY) != null - ? ((ScanConsistency) meta.get(SCAN_CONSISTENCY)).query() - : null; - QueryScanConsistency qsc = fromFirst(QueryScanConsistency.NOT_BOUNDED, getScanConsistency(optsJson), - scanConsistency, metaQueryScanConsistency); - Duration timeout = fromFirst(Duration.ofSeconds(0), getTimeout(optsBuilt), meta.get(TIMEOUT)); - RetryStrategy retryStrategy = fromFirst(null, getRetryStrategy(optsBuilt), meta.get(RETRY_STRATEGY)); - - if (qsc != null) { - options.scanConsistency(qsc); - } - if (timeout != null) { - options.timeout(timeout); - } - if (retryStrategy != null) { - options.retryStrategy(retryStrategy); - } - if (LOG.isTraceEnabled()) { - LOG.trace("query options: {}", getQueryOpts(options.build())); - } - return options; - } - - public static ExistsOptions buildExistsOptions(ExistsOptions options) { - options = options != null ? options : ExistsOptions.existsOptions(); - return options; - } - - public static InsertOptions buildInsertOptions(InsertOptions options, PersistTo persistTo, ReplicateTo replicateTo, - DurabilityLevel durabilityLevel, Duration expiry, CouchbaseDocument doc) { - options = options != null ? options : InsertOptions.insertOptions(); - if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) { - options.durability(persistTo, replicateTo); - } else if (durabilityLevel != DurabilityLevel.NONE) { - options.durability(durabilityLevel); - } - if (expiry != null) { - options.expiry(expiry); - } else if (doc.getExpiration() != 0) { - options.expiry(Duration.ofSeconds(doc.getExpiration())); - } - if (LOG.isTraceEnabled()) { - LOG.trace("insert options: {}" + toString(options)); - } - return options; - } - - public static UpsertOptions buildUpsertOptions(UpsertOptions options, PersistTo persistTo, ReplicateTo replicateTo, - DurabilityLevel durabilityLevel, Duration expiry, CouchbaseDocument doc) { - options = options != null ? options : UpsertOptions.upsertOptions(); - if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) { - options.durability(persistTo, replicateTo); - } else if (durabilityLevel != DurabilityLevel.NONE) { - options.durability(durabilityLevel); - } - if (expiry != null) { - options.expiry(expiry); - } else if (doc.getExpiration() != 0) { - options.expiry(Duration.ofSeconds(doc.getExpiration())); - } - if (LOG.isTraceEnabled()) { - LOG.trace("upsert options: {}" + toString(options)); - } - return options; - } - - public static ReplaceOptions buildReplaceOptions(ReplaceOptions options, PersistTo persistTo, ReplicateTo replicateTo, - DurabilityLevel durabilityLevel, Duration expiry, Long cas, CouchbaseDocument doc) { - options = options != null ? options : ReplaceOptions.replaceOptions(); - if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) { - options.durability(persistTo, replicateTo); - } else if (durabilityLevel != DurabilityLevel.NONE) { - options.durability(durabilityLevel); - } - if (expiry != null) { - options.expiry(expiry); - } else if (doc.getExpiration() != 0) { - options.expiry(Duration.ofSeconds(doc.getExpiration())); - } - if (cas != null) { - options.cas(cas); - } - if (LOG.isTraceEnabled()) { - LOG.trace("replace options: {}" + toString(options)); - } - return options; - } - - public static RemoveOptions buildRemoveOptions(RemoveOptions options, PersistTo persistTo, ReplicateTo replicateTo, - DurabilityLevel durabilityLevel, Long cas) { - options = options != null ? options : RemoveOptions.removeOptions(); - if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) { - options.durability(persistTo, replicateTo); - } else if (durabilityLevel != DurabilityLevel.NONE) { - options.durability(durabilityLevel); - } - RemoveOptions.Built optsBuilt = options.build(); - Duration timeout = fromFirst(Duration.ofSeconds(0), optsBuilt.timeout()); - RetryStrategy retryStrategy = fromFirst(null, optsBuilt.retryStrategy()); - - if (timeout != null) { - options.timeout(timeout); - } - if (retryStrategy != null) { - options.retryStrategy(retryStrategy); - } - if (cas != null) { - options.cas(cas); - } - if (LOG.isTraceEnabled()) { - LOG.trace("remove options: {}" + toString(options)); - } - return options; - } - - /** - * scope annotation could be a - * - * @param domainType - * @return - */ - public static String getScopeFrom(Class domainType) { - if (domainType == null) { - return null; - } - Scope ann = AnnotatedElementUtils.findMergedAnnotation(domainType, Scope.class); - if (ann != null && !CollectionIdentifier.DEFAULT_COLLECTION.equals(ann.value())) { - return ann.value(); - } - return null; - } - - public static String getCollectionFrom(Class domainType) { - if (domainType == null) { - return null; - } - Collection ann = AnnotatedElementUtils.findMergedAnnotation(domainType, Collection.class); - if (ann != null && !CollectionIdentifier.DEFAULT_COLLECTION.equals(ann.value())) { - return ann.value(); - } - return null; - } - - static String toString(InsertOptions o) { - StringBuilder s = new StringBuilder(); - InsertOptions.Built b = o.build(); - s.append("{"); - s.append("durabilityLevel: " + b.durabilityLevel()); - s.append(", persistTo: " + b.persistTo()); - s.append(", replicateTo: " + b.replicateTo()); - s.append(", timeout: " + b.timeout()); - s.append(", retryStrategy: " + b.retryStrategy()); - s.append(", clientContext: " + b.clientContext()); - s.append(", parentSpan: " + b.parentSpan()); - s.append("}"); - return s.toString(); - } - - static String toString(UpsertOptions o) { - StringBuilder s = new StringBuilder(); - UpsertOptions.Built b = o.build(); - s.append("{"); - s.append("durabilityLevel: " + b.durabilityLevel()); - s.append(", persistTo: " + b.persistTo()); - s.append(", replicateTo: " + b.replicateTo()); - s.append(", timeout: " + b.timeout()); - s.append(", retryStrategy: " + b.retryStrategy()); - s.append(", clientContext: " + b.clientContext()); - s.append(", parentSpan: " + b.parentSpan()); - s.append("}"); - return s.toString(); - } - - static String toString(ReplaceOptions o) { - StringBuilder s = new StringBuilder(); - ReplaceOptions.Built b = o.build(); - s.append("{"); - s.append("cas: " + b.cas()); - s.append(", durabilityLevel: " + b.durabilityLevel()); - s.append(", persistTo: " + b.persistTo()); - s.append(", replicateTo: " + b.replicateTo()); - s.append(", timeout: " + b.timeout()); - s.append(", retryStrategy: " + b.retryStrategy()); - s.append(", clientContext: " + b.clientContext()); - s.append(", parentSpan: " + b.parentSpan()); - s.append("}"); - return s.toString(); - } - - static String toString(RemoveOptions o) { - StringBuilder s = new StringBuilder(); - RemoveOptions.Built b = o.build(); - s.append("{"); - s.append("cas: " + b.cas()); - s.append(", durabilityLevel: " + b.durabilityLevel()); - s.append(", persistTo: " + b.persistTo()); - s.append(", replicateTo: " + b.replicateTo()); - s.append(", timeout: " + b.timeout()); - s.append(", retryStrategy: " + b.retryStrategy()); - s.append(", clientContext: " + b.clientContext()); - s.append(", parentSpan: " + b.parentSpan()); - s.append("}"); - return s.toString(); - } - - private static JsonObject getQueryOpts(QueryOptions.Built optsBuilt) { - JsonObject jo = JsonObject.create(); - optsBuilt.injectParams(jo); - return jo; - } - - /** - * Get the most-specific - * - * @param deflt the default value, which we treat as not set - * @param choice array of values or Optional<values>, ordered from most to least specific - * @param - * @return the most specific choice - */ - public static T fromFirst(T deflt, Object... choice) { - T chosen = choice[0] instanceof Optional ? ((Optional) choice[0]).orElse(null) : (T) choice[0]; - for (int i = 1; i < choice.length; i++) { - if (chosen == null || chosen.equals(deflt)) { // overwrite null or default... - if (choice[i] != null) { // ... with non-null - chosen = choice[i] instanceof Optional ? ((Optional) choice[i]).orElse(null) : (T) choice[i]; - } - } - } - return chosen; - } - - private static QueryScanConsistency getScanConsistency(JsonObject opts) { - String str = opts.getString("scan_consistency"); - if ("at_plus".equals(str)) { - return null; - } - return str == null ? null : QueryScanConsistency.valueOf(str.toUpperCase()); - } - - private static JsonObject getScanVectors(JsonObject opts) { - return opts.getObject("scan_vectors"); - } - - private static Duration getTimeout(QueryOptions.Built optsBuilt) { - Optional timeout = optsBuilt.timeout(); - return timeout.isPresent() ? timeout.get() : null; - } - - private static RetryStrategy getRetryStrategy(QueryOptions.Built optsBuilt) { - Optional retryStrategy = optsBuilt.retryStrategy(); - return retryStrategy.isPresent() ? retryStrategy.get() : null; - } - - public static Meta buildMeta(CouchbaseQueryMethod method, Class typeToRead) { - Meta meta = new Meta(); - // Scope and Collection annotations are handled in PseudArgs - // this would include a ScanConsistency in a composed annotation as well. - meta.set(SCAN_CONSISTENCY, method.getScanConsistencyAnnotation()); - return meta; - } - - /** - * return the first merged annotation which does not have attribute with null/defaultValue from the listed elements. - * - * @param - * @param annotation - * @param attributeName - * @param defaultValue - * @param elements - * @return - */ - public static A annotation(Class annotation, String attributeName, V defaultValue, - AnnotatedElement... elements) { - int i = 1; - for (AnnotatedElement el : elements) { - A an = AnnotatedElementUtils.findMergedAnnotation(el, annotation); - if (an != null) { - if (defaultValue != null) { - try { - Method m = an.getClass().getMethod(attributeName); - V value = (V) m.invoke(an); - if (!defaultValue.equals(value)) { - return an; - } - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - } else { - return an; - } - } - } - return null; - } - - public static A annotation(Class annotation, V defaultValue, - AnnotatedElement[] elements) { - return annotation(annotation, "value", defaultValue, elements); - } - - /** - * return the first merged annotation which is not null/defaultValue from the listed elements. - * - * @param - * @param annotation - * @param defaultValue - * @param elements - * @return - */ - public static V annotationAttribute(Class annotation, String attributeName, - V defaultValue, AnnotatedElement[] elements) { - for (AnnotatedElement el : elements) { - A an = AnnotatedElementUtils.findMergedAnnotation(el, annotation); - if (an != null) { - if (defaultValue != null && !defaultValue.equals(an)) { - try { - Method m = an.getClass().getMethod(attributeName); - return (V) m.invoke(an); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - } - } - } - return null; - } - - /** - * return the toString() of the first merged annotation which is not null/defaultValue from the listed elements. - * - * @param annotation - * @param defaultValue - * @param elements - * @param - * @return - */ - public static String annotationString(Class annotation, String attributeName, - Object defaultValue, AnnotatedElement[] elements) { - A result = annotation(annotation, defaultValue, elements); - if (result == null) { - return null; - } - try { - Method m = result.getClass().getMethod(attributeName); - Object value = m.invoke(result); - return value.toString(); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - } - - public static String annotationString(Class annotation, Object defaultValue, - AnnotatedElement[] elements) { - return annotationString(annotation, "value", defaultValue, elements); - } -} diff --git a/src/main/java/org/springframework/data/couchbase/core/query/Query.java b/src/main/java/org/springframework/data/couchbase/core/query/Query.java index d6d24c97..62b55aca 100644 --- a/src/main/java/org/springframework/data/couchbase/core/query/Query.java +++ b/src/main/java/org/springframework/data/couchbase/core/query/Query.java @@ -25,7 +25,6 @@ import org.slf4j.LoggerFactory; import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate; import org.springframework.data.couchbase.core.convert.CouchbaseConverter; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity; -import org.springframework.data.couchbase.repository.query.CouchbaseQueryMethod; import org.springframework.data.couchbase.repository.query.StringBasedN1qlQueryParser; import org.springframework.data.couchbase.repository.support.MappingCouchbaseEntityInformation; import org.springframework.data.domain.Pageable; @@ -53,7 +52,6 @@ public class Query { private int limit; private Sort sort = Sort.unsorted(); private QueryScanConsistency queryScanConsistency; - private Meta meta; static private final Pattern WHERE_PATTERN = Pattern.compile("\\sWHERE\\s"); private static final Logger LOG = LoggerFactory.getLogger(Query.class); @@ -328,22 +326,28 @@ public class Query { * @return QueryOptions */ public QueryOptions buildQueryOptions(QueryOptions options, QueryScanConsistency scanConsistency) { - return OptionsBuilder.buildQueryOptions(this, options, scanConsistency); + if (options == null) { // add/override what we got from PseudoArgs + options = QueryOptions.queryOptions(); + } + if (getParameters() != null) { + if (getParameters() instanceof JsonArray) { + options.parameters((JsonArray) getParameters()); + } else { + options.parameters((JsonObject) getParameters()); + } + } + if (scanConsistency == null + || scanConsistency == QueryScanConsistency.NOT_BOUNDED && getScanConsistency() != null) { + scanConsistency = getScanConsistency(); + } + if (scanConsistency != null) { + options.scanConsistency(scanConsistency); + } + return options; } - /** - * this collections annotations from the method, repository class and possibly the entity class to be used as options. - * This will find annotations included in composed annotations as well. Ideally - * - * @param method representing the query. - * @return the query with the annotations applied - */ - public void setMeta(CouchbaseQueryMethod method, Class typeToRead) { - meta = OptionsBuilder.buildMeta(method, typeToRead); - } - - public Meta getMeta() { - return meta; + public void setMeta(Meta metaAnnotation) { + Meta meta = metaAnnotation; } } diff --git a/src/main/java/org/springframework/data/couchbase/core/support/PseudoArgs.java b/src/main/java/org/springframework/data/couchbase/core/support/PseudoArgs.java index fb1bc9da..cb92bbc2 100644 --- a/src/main/java/org/springframework/data/couchbase/core/support/PseudoArgs.java +++ b/src/main/java/org/springframework/data/couchbase/core/support/PseudoArgs.java @@ -15,13 +15,8 @@ */ package org.springframework.data.couchbase.core.support; -import static org.springframework.data.couchbase.core.query.OptionsBuilder.fromFirst; -import static org.springframework.data.couchbase.core.query.OptionsBuilder.getCollectionFrom; -import static org.springframework.data.couchbase.core.query.OptionsBuilder.getScopeFrom; - -import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate; - import com.couchbase.client.core.io.CollectionIdentifier; +import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate; public class PseudoArgs { private final OPTS options; @@ -40,32 +35,24 @@ public class PseudoArgs { * 2) values from dynamic proxy (via template threadLocal)
* 3) the values from the couchbaseClientFactory
* - * @param template which holds ThreadLocal pseudo args - * @param scope - from calling operation - * @param collection - from calling operation - * @param options - from calling operation - * @param domainType - entity that may have annotations + * @param template to hold + * @param scope + * @param collection + * @param options */ - public PseudoArgs(ReactiveCouchbaseTemplate template, String scope, String collection, OPTS options, - Class domainType) { + public PseudoArgs(ReactiveCouchbaseTemplate template, String scope, String collection, OPTS options) { - String scopeForQuery = null; - String collectionForQuery = null; - OPTS optionsForQuery = null; + // 1) values from the args (fluent api) - // 1) repository from DynamicProxy via template threadLocal - has precedence over annotation + String scopeForQuery = scope; + String collectionForQuery = collection; + OPTS optionsForQuery = options; - PseudoArgs threadLocal = (PseudoArgs) template.getPseudoArgs(); - template.setPseudoArgs(null); - if (threadLocal != null) { - scopeForQuery = threadLocal.getScope(); - collectionForQuery = threadLocal.getCollection(); - optionsForQuery = threadLocal.getOptions(); - } + // 2) from DynamicProxy via template threadLocal - scopeForQuery = fromFirst(null, scopeForQuery, scope, getScopeFrom(domainType)); - collectionForQuery = fromFirst(null, collectionForQuery, collection, getCollectionFrom(domainType)); - optionsForQuery = fromFirst(null, options, optionsForQuery); + scopeForQuery = scopeForQuery != null ? scopeForQuery : getThreadLocalScopeName(template); + collectionForQuery = collectionForQuery != null ? collectionForQuery : getThreadLocalCollectionName(template); + optionsForQuery = optionsForQuery != null ? optionsForQuery : getThreadLocalOptions(template); // if a collection was specified but no scope, use the scope from the clientFactory @@ -75,42 +62,56 @@ public class PseudoArgs { // specifying scope and collection = _default is not necessary and will fail if server doesn't have collections - if (scopeForQuery == null || CollectionIdentifier.DEFAULT_SCOPE.equals(scopeForQuery)) { - if (collectionForQuery == null || CollectionIdentifier.DEFAULT_COLLECTION.equals(collectionForQuery)) { - collectionForQuery = null; - scopeForQuery = null; - } + if ((scopeForQuery == null || CollectionIdentifier.DEFAULT_SCOPE.equals(scopeForQuery)) + && (collectionForQuery == null || CollectionIdentifier.DEFAULT_COLLECTION.equals(collectionForQuery))) { + scopeForQuery = null; + collectionForQuery = null; } this.scopeName = scopeForQuery; this.collectionName = collectionForQuery; this.options = optionsForQuery; - } /** - * @return the options + * @@return the options */ public OPTS getOptions() { return this.options; } /** - * @return the scope name + * @@return the scope name */ public String getScope() { return this.scopeName; } /** - * @return the collection name + * @@return the collection name */ public String getCollection() { return this.collectionName; } - @Override - public String toString() { - return "scope: " + getScope() + " collection: " + getCollection() + " options: " + getOptions(); + /** + * @@return the options from the ThreadLocal field of the template + */ + private OPTS getThreadLocalOptions(ReactiveCouchbaseTemplate template) { + return template.getPseudoArgs() == null ? null : (OPTS) (template.getPseudoArgs().getOptions()); + } + + /** + * @@return the scope name from the ThreadLocal field of the template + */ + private String getThreadLocalScopeName(ReactiveCouchbaseTemplate template) { + return template.getPseudoArgs() == null ? null : template.getPseudoArgs().getScope(); + } + + /** + * @@return the collection name from the ThreadLocal field of the template + */ + private String getThreadLocalCollectionName(ReactiveCouchbaseTemplate template) { + return template.getPseudoArgs() == null ? null : template.getPseudoArgs().getCollection(); } } diff --git a/src/main/java/org/springframework/data/couchbase/repository/Collection.java b/src/main/java/org/springframework/data/couchbase/repository/Collection.java deleted file mode 100644 index 6734902b..00000000 --- a/src/main/java/org/springframework/data/couchbase/repository/Collection.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2012-2021 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.couchbase.repository; - -import static com.couchbase.client.core.io.CollectionIdentifier.DEFAULT_COLLECTION; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.springframework.data.annotation.QueryAnnotation; - -/** - * Collection Annotation - * - * @author Michael Reiche - */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE }) -@Documented -@QueryAnnotation -public @interface Collection { - - /** - * Specifies the collection name - * - * @return the collection name configured, defaults to not DEFAULT_COLLECTION. - */ - String value() default DEFAULT_COLLECTION; - -} diff --git a/src/main/java/org/springframework/data/couchbase/repository/CouchbaseRepository.java b/src/main/java/org/springframework/data/couchbase/repository/CouchbaseRepository.java index f14bca46..5544d7e5 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/CouchbaseRepository.java +++ b/src/main/java/org/springframework/data/couchbase/repository/CouchbaseRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,20 +18,16 @@ package org.springframework.data.couchbase.repository; import java.util.List; -import org.springframework.data.couchbase.core.CouchbaseOperations; -import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation; +import com.couchbase.client.java.query.QueryScanConsistency; import org.springframework.data.domain.Sort; import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.Repository; -import com.couchbase.client.java.query.QueryScanConsistency; - /** * Couchbase specific {@link Repository} interface. * * @author Michael Nitschinger - * @author Michael Reiche */ @NoRepositoryBean public interface CouchbaseRepository extends PagingAndSortingRepository { @@ -47,8 +43,4 @@ public interface CouchbaseRepository extends PagingAndSortingRepository findAllById(Iterable iterable); - CouchbaseEntityInformation getEntityInformation(); - - CouchbaseOperations getOperations(); - } diff --git a/src/main/java/org/springframework/data/couchbase/repository/DynamicProxyable.java b/src/main/java/org/springframework/data/couchbase/repository/DynamicProxyable.java deleted file mode 100644 index c97f9b9e..00000000 --- a/src/main/java/org/springframework/data/couchbase/repository/DynamicProxyable.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2017-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.data.couchbase.repository; - -import java.lang.reflect.Proxy; - -import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation; -import org.springframework.data.couchbase.repository.support.DynamicInvocationHandler; - -import com.couchbase.client.java.CommonOptions; - -/** - * The generic parameter needs to be REPO which is either a CouchbaseRepository parameterized on T,ID or a - * ReactiveCouchbaseRepository parameterized on T,ID. i.e.: interface AirportRepository extends - * CouchbaseRepository, DynamicProxyable - * - * @param - * @author Michael Reiche - */ -public interface DynamicProxyable { - - CouchbaseEntityInformation getEntityInformation(); - - Object getOperations(); - - /** - * Support for Couchbase-specific options, scope and collections The three "with" methods will return a new proxy - * instance with the specified options, scope, or collections set. The setters are called with the corresponding - * options, scope and collection to set the ThreadLocal fields on the CouchbaseOperations of the repository just - * before the call is made to the repository, and called again with 'null' just after the call is made. The repository - * method will fetch those values to use in the call. - */ - - /** - * @param options - the options to set on the returned repository object - */ - @SuppressWarnings("unchecked") - default REPO withOptions(CommonOptions options) { - REPO proxyInstance = (REPO) Proxy.newProxyInstance(this.getClass().getClassLoader(), - this.getClass().getInterfaces(), new DynamicInvocationHandler(this, options, null, (String) null)); - return proxyInstance; - } - - /** - * @param scope - the scope to set on the returned repository object - */ - @SuppressWarnings("unchecked") - default REPO withScope(String scope) { - REPO proxyInstance = (REPO) Proxy.newProxyInstance(this.getClass().getClassLoader(), - this.getClass().getInterfaces(), new DynamicInvocationHandler<>(this, null, null, scope)); - return proxyInstance; - } - - /** - * @param collection - the collection to set on the returned repository object - */ - @SuppressWarnings("unchecked") - default REPO withCollection(String collection) { - REPO proxyInstance = (REPO) Proxy.newProxyInstance(this.getClass().getClassLoader(), - this.getClass().getInterfaces(), new DynamicInvocationHandler<>(this, null, collection, null)); - return proxyInstance; - } - -} diff --git a/src/main/java/org/springframework/data/couchbase/repository/Scope.java b/src/main/java/org/springframework/data/couchbase/repository/Meta.java similarity index 66% rename from src/main/java/org/springframework/data/couchbase/repository/Scope.java rename to src/main/java/org/springframework/data/couchbase/repository/Meta.java index 8c146b07..298379ee 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/Scope.java +++ b/src/main/java/org/springframework/data/couchbase/repository/Meta.java @@ -1,11 +1,11 @@ /* - * Copyright 2012-2021 the original author or authors + * Copyright 2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -15,8 +15,6 @@ */ package org.springframework.data.couchbase.repository; -import static com.couchbase.client.core.io.CollectionIdentifier.DEFAULT_SCOPE; - import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -26,21 +24,13 @@ import java.lang.annotation.Target; import org.springframework.data.annotation.QueryAnnotation; /** - * Scope Annotation - * * @author Michael Reiche + * @since 4.1 */ @Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE }) +@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Documented @QueryAnnotation -public @interface Scope { - - /** - * Specifies the scope name - * - * @return the scope name configured, defaults to DEFAULT_SCOPE. - */ - String value() default DEFAULT_SCOPE; +public @interface Meta { } diff --git a/src/main/java/org/springframework/data/couchbase/repository/Options.java b/src/main/java/org/springframework/data/couchbase/repository/Options.java deleted file mode 100644 index 07054c10..00000000 --- a/src/main/java/org/springframework/data/couchbase/repository/Options.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2012-2021 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.couchbase.repository; - -import static com.couchbase.client.core.io.CollectionIdentifier.DEFAULT_COLLECTION; -import static com.couchbase.client.core.io.CollectionIdentifier.DEFAULT_SCOPE; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.concurrent.TimeUnit; - -import org.springframework.data.annotation.QueryAnnotation; - -import com.couchbase.client.java.analytics.AnalyticsScanConsistency; -import com.couchbase.client.java.query.QueryScanConsistency; - -/** - * Scope Annotation - * - * @author Michael Reiche - */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE }) -@Documented -@QueryAnnotation -public @interface Options { - - /** - * Specifies the scope name - * - * @return the scope name configured, defaults to not DEFAULT_SCOPE. - */ - String scope() default DEFAULT_SCOPE; - - /** - * Specifies the scope name - * - * @return the scope name configured, defaults to not DEFAULT_SCOPE. - */ - String collection() default DEFAULT_COLLECTION; - - /** - * Specifies a custom scan consistency for N1QL queries. - * - * @return the scan consistency configured, defaults to not bounded. - */ - QueryScanConsistency query() default QueryScanConsistency.NOT_BOUNDED; - - /** - * Specifies a custom scan consistency for analytics queries. - * - * @return the scan consistency configured, defaults to not bounded. - */ - AnalyticsScanConsistency analytics() default AnalyticsScanConsistency.NOT_BOUNDED; - - /** - * Specifies a custom projection. - * - * @return the projection configured, defaults to an empty array (project everything). - */ - String[] project() default {}; - - /** - * Specifies a custom array of distinct fields. - * - * @return the projection configured, we need to do something tricky with the default. We need to default to
- * no distinct, which is specified by a null array (an empty array means distinct on everything). We'll make - * an array of a single element "-" mean no distinct. - */ - String[] distinct() default { "-" }; - - /** - * An optional expiry time for the document. Default is no expiry. Only one of two might might be set at the same - * time: either {@link #expiry()} or {@link #expiryExpression()} - */ - int expiry() default 0; - - /** - * Same as {@link #expiry} but allows the actual value to be set using standard Spring property sources mechanism. - * Only one might be set at the same time: either {@link #expiry()} or {@link #expiryExpression()}.
- * Syntax is the same as for {@link org.springframework.core.env.Environment#resolveRequiredPlaceholders(String)}. - *
- *
- * The value will be recalculated for every {@link org.springframework.data.couchbase.core.CouchbaseTemplate} - * save/insert/update call, thus allowing actual expiration to reflect changes on-the-fly as soon as property sources - * change.
- *
- * SpEL is NOT supported. - */ - String expiryExpression() default ""; - - /** - * An optional time unit for the document's {@link #expiry()}, if set. Default is {@link TimeUnit#SECONDS}. - */ - TimeUnit expiryUnit() default TimeUnit.SECONDS; - - /** - * An timeout for the operation. Default is no timeout. - */ - long timeoutMs() default 0; -} diff --git a/src/main/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepository.java b/src/main/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepository.java index db41820f..d76af65f 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepository.java +++ b/src/main/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +15,6 @@ */ package org.springframework.data.couchbase.repository; -import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations; -import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation; import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.reactive.ReactiveSortingRepository; @@ -24,12 +22,9 @@ import org.springframework.data.repository.reactive.ReactiveSortingRepository; * Couchbase-specific {@link ReactiveSortingRepository} implementation. * * @author Subhashni Balakrishnan - * @author Michael Reiche * @since 3.0 */ @NoRepositoryBean public interface ReactiveCouchbaseRepository extends ReactiveSortingRepository { - ReactiveCouchbaseOperations getOperations(); - CouchbaseEntityInformation getEntityInformation(); } diff --git a/src/main/java/org/springframework/data/couchbase/repository/ScanConsistency.java b/src/main/java/org/springframework/data/couchbase/repository/ScanConsistency.java index 961358d9..7254e1ae 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/ScanConsistency.java +++ b/src/main/java/org/springframework/data/couchbase/repository/ScanConsistency.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors + * Copyright 2012-2020 the original author or authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,13 +24,8 @@ import java.lang.annotation.Target; import com.couchbase.client.java.analytics.AnalyticsScanConsistency; import com.couchbase.client.java.query.QueryScanConsistency; -/** - * Scan Consistency Annotation - * - * @author Michael Reiche - */ @Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE }) +@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Documented public @interface ScanConsistency { diff --git a/src/main/java/org/springframework/data/couchbase/repository/auditing/ReactiveCouchbaseAuditingRegistrar.java b/src/main/java/org/springframework/data/couchbase/repository/auditing/ReactiveCouchbaseAuditingRegistrar.java index 25b22026..a38cbcbf 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/auditing/ReactiveCouchbaseAuditingRegistrar.java +++ b/src/main/java/org/springframework/data/couchbase/repository/auditing/ReactiveCouchbaseAuditingRegistrar.java @@ -1,18 +1,3 @@ -/* - * Copyright 2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package org.springframework.data.couchbase.repository.auditing; import static org.springframework.data.couchbase.config.BeanNames.REACTIVE_COUCHBASE_AUDITING_HANDLER; @@ -28,6 +13,7 @@ import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarS import org.springframework.data.auditing.config.AuditingConfiguration; import org.springframework.data.config.ParsingUtils; import org.springframework.data.couchbase.core.mapping.event.ReactiveAuditingEntityCallback; +import org.springframework.data.couchbase.core.mapping.event.ReactiveAuditingEventListener; import org.springframework.util.Assert; /** @@ -96,6 +82,17 @@ class ReactiveCouchbaseAuditingRegistrar extends AuditingBeanDefinitionRegistrar registerInfrastructureBeanWithId(builder.getBeanDefinition(), ReactiveAuditingEntityCallback.class.getName(), registry); + // Register the AuditingEventListener + + BeanDefinitionBuilder builder2 = BeanDefinitionBuilder.rootBeanDefinition(ReactiveAuditingEventListener.class); + + builder2 + .addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry)); + builder.getRawBeanDefinition().setSource(auditingHandlerDefinition.getSource()); + + registerInfrastructureBeanWithId(builder2.getBeanDefinition(), ReactiveAuditingEventListener.class.getName(), + registry); + } } diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/AbstractCouchbaseQuery.java b/src/main/java/org/springframework/data/couchbase/repository/query/AbstractCouchbaseQuery.java index 9dc3fe51..979de664 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/query/AbstractCouchbaseQuery.java +++ b/src/main/java/org/springframework/data/couchbase/repository/query/AbstractCouchbaseQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors + * Copyright 2020 the original author or authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ package org.springframework.data.couchbase.repository.query; +import com.couchbase.client.core.io.CollectionIdentifier; import org.springframework.core.convert.converter.Converter; import org.springframework.data.couchbase.core.CouchbaseOperations; import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation.ExecutableFindByQuery; @@ -59,11 +60,10 @@ public abstract class AbstractCouchbaseQuery extends AbstractCouchbaseQueryBase< Assert.notNull(operations, "ReactiveCouchbaseOperations must not be null!"); Assert.notNull(expressionParser, "SpelExpressionParser must not be null!"); Assert.notNull(evaluationContextProvider, "QueryMethodEvaluationContextProvider must not be null!"); + // this.operations = operations; EntityMetadata metadata = method.getEntityInformation(); Class type = metadata.getJavaType(); - ExecutableFindByQuery findOp = operations.findByQuery(type); - findOp = (ExecutableFindByQuery) (findOp.inScope(method.getScope()).inCollection(method.getCollection())); - this.findOperationWithProjection = findOp; + this.findOperationWithProjection = operations.findByQuery(type); } /** @@ -80,14 +80,18 @@ public abstract class AbstractCouchbaseQuery extends AbstractCouchbaseQueryBase< ParametersParameterAccessor accessor, @Nullable Class typeToRead) { Query query = createQuery(accessor); - // query = applyAnnotatedCollationIfPresent(query, accessor); // not yet implemented - query = applyQueryMetaAttributesIfPresent(query, typeToRead); - ExecutableFindByQuery find = findOperationWithProjection; + query = applyAnnotatedConsistencyIfPresent(query); + // query = applyAnnotatedCollationIfPresent(query, accessor); // not yet implemented + + ExecutableFindByQuery find = typeToRead == null ? findOperationWithProjection // + : findOperationWithProjection; // not yet implemented in core .as(typeToRead); + + String collection = null; CouchbaseQueryExecution execution = getExecution(accessor, new ResultProcessingConverter<>(processor, getOperations(), getInstantiators()), find); - return execution.execute(query, processor.getReturnedType().getDomainType(), null); + return execution.execute(query, processor.getReturnedType().getDomainType(), collection); } /** @@ -110,7 +114,8 @@ public abstract class AbstractCouchbaseQuery extends AbstractCouchbaseQueryBase< * @param operation must not be {@literal null}. * @return */ - private CouchbaseQueryExecution getExecutionToWrap(ParameterAccessor accessor, ExecutableFindByQuery operation) { + private CouchbaseQueryExecution getExecutionToWrap(ParameterAccessor accessor, + ExecutableFindByQuery operation) { if (isDeleteQuery()) { return new DeleteExecution(getOperations(), getQueryMethod()); @@ -118,8 +123,6 @@ public abstract class AbstractCouchbaseQuery extends AbstractCouchbaseQueryBase< return (q, t, c) -> operation.matching(q.with(accessor.getPageable())).all(); // s/b tail() instead of all() } else if (getQueryMethod().isCollectionQuery()) { return (q, t, c) -> operation.matching(q.with(accessor.getPageable())).all(); - } else if (getQueryMethod().isStreamQuery()) { - return (q, t, c) -> operation.matching(q.with(accessor.getPageable())).stream(); } else if (isCountQuery()) { return (q, t, c) -> operation.matching(q).count(); } else if (isExistsQuery()) { @@ -137,4 +140,18 @@ public abstract class AbstractCouchbaseQuery extends AbstractCouchbaseQueryBase< } } + /** + * Apply Meta annotation to query + * + * @param query must not be {@literal null}. + * @return Query + */ + Query applyQueryMetaAttributesWhenPresent(Query query) { + + if (getQueryMethod().hasQueryMetaAttributes()) { + query.setMeta(getQueryMethod().getQueryMetaAttributes()); + } + + return query; + } } diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/AbstractCouchbaseQueryBase.java b/src/main/java/org/springframework/data/couchbase/repository/query/AbstractCouchbaseQueryBase.java index b00101e5..9314371d 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/query/AbstractCouchbaseQueryBase.java +++ b/src/main/java/org/springframework/data/couchbase/repository/query/AbstractCouchbaseQueryBase.java @@ -146,14 +146,18 @@ public abstract class AbstractCouchbaseQueryBase implem ParametersParameterAccessor accessor, @Nullable Class typeToRead); /** - * Apply Meta annotation to query + * Add a scan consistency from {@link org.springframework.data.couchbase.repository.ScanConsistency} to the given + * {@link Query} if present. * - * @param query must not be {@literal null}. - * @return Query + * @param query the {@link Query} to potentially apply the sort to. + * @return the query with potential scan consistency applied. + * @since 4.1 */ - Query applyQueryMetaAttributesIfPresent(Query query, Class typeToRead) { - query.setMeta(getQueryMethod(), typeToRead); - return query; + Query applyAnnotatedConsistencyIfPresent(Query query) { + if (!method.hasScanConsistencyAnnotation()) { + return query; + } + return query.scanConsistency(method.getScanConsistencyAnnotation().query()); } /** diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/AbstractReactiveCouchbaseQuery.java b/src/main/java/org/springframework/data/couchbase/repository/query/AbstractReactiveCouchbaseQuery.java index 8b04d292..f01e3f6c 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/query/AbstractReactiveCouchbaseQuery.java +++ b/src/main/java/org/springframework/data/couchbase/repository/query/AbstractReactiveCouchbaseQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ package org.springframework.data.couchbase.repository.query; +import com.couchbase.client.core.io.CollectionIdentifier; import org.springframework.core.convert.converter.Converter; import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations; import org.springframework.data.couchbase.core.ReactiveFindByQueryOperation; @@ -62,9 +63,7 @@ public abstract class AbstractReactiveCouchbaseQuery extends AbstractCouchbaseQu EntityMetadata metadata = method.getEntityInformation(); Class type = metadata.getJavaType(); - ReactiveFindByQuery findOp = operations.findByQuery(type); - findOp = (ReactiveFindByQuery) (findOp.inScope(method.getScope()).inCollection(method.getCollection())); - this.findOperationWithProjection = findOp; + this.findOperationWithProjection = operations.findByQuery(type); } /** @@ -80,14 +79,18 @@ public abstract class AbstractReactiveCouchbaseQuery extends AbstractCouchbaseQu ParametersParameterAccessor accessor, @Nullable Class typeToRead) { Query query = createQuery(accessor); + query = applyAnnotatedConsistencyIfPresent(query); // query = applyAnnotatedCollationIfPresent(query, accessor); // not yet implemented - query = applyQueryMetaAttributesIfPresent(query, typeToRead); - ReactiveFindByQuery find = findOperationWithProjection; + ReactiveFindByQuery find = typeToRead == null // + ? findOperationWithProjection // + : findOperationWithProjection; // note yet implemented in core .as(typeToRead); + + String collection = null; ReactiveCouchbaseQueryExecution execution = getExecution(accessor, new ResultProcessingConverter<>(processor, getOperations(), getInstantiators()), find); - return execution.execute(query, processor.getReturnedType().getDomainType(), null); + return execution.execute(query, processor.getReturnedType().getDomainType(), collection); } /** @@ -118,8 +121,6 @@ public abstract class AbstractReactiveCouchbaseQuery extends AbstractCouchbaseQu return (q, t, c) -> operation.matching(q.with(accessor.getPageable())).all(); // s/b tail() instead of all() } else if (getQueryMethod().isCollectionQuery()) { return (q, t, c) -> operation.matching(q.with(accessor.getPageable())).all(); - // } else if (getQueryMethod().isStreamQuery()) { - // return (q, t, c) -> operation.matching(q.with(accessor.getPageable())).all().toStream(); } else if (isCountQuery()) { return (q, t, c) -> operation.matching(q).count(); } else if (isExistsQuery()) { @@ -132,4 +133,18 @@ public abstract class AbstractReactiveCouchbaseQuery extends AbstractCouchbaseQu } } + /** + * Apply Meta annotation to query + * + * @param query must not be {@literal null}. + * @return Query + */ + Query applyQueryMetaAttributesWhenPresent(Query query) { + + if (getQueryMethod().hasQueryMetaAttributes()) { + query.setMeta(getQueryMethod().getQueryMetaAttributes()); + } + + return query; + } } diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/CouchbaseQueryMethod.java b/src/main/java/org/springframework/data/couchbase/repository/query/CouchbaseQueryMethod.java index b4141951..86243473 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/query/CouchbaseQueryMethod.java +++ b/src/main/java/org/springframework/data/couchbase/repository/query/CouchbaseQueryMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,33 +16,28 @@ package org.springframework.data.couchbase.repository.query; -import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.Locale; -import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty; import org.springframework.data.couchbase.core.query.Dimensional; -import org.springframework.data.couchbase.core.query.OptionsBuilder; import org.springframework.data.couchbase.core.query.View; import org.springframework.data.couchbase.core.query.WithConsistency; -import org.springframework.data.couchbase.repository.Collection; +import org.springframework.data.couchbase.repository.Meta; import org.springframework.data.couchbase.repository.Query; import org.springframework.data.couchbase.repository.ScanConsistency; -import org.springframework.data.couchbase.repository.Scope; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.QueryMethod; import org.springframework.data.repository.util.ReactiveWrapperConverters; +import org.springframework.lang.Nullable; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; -import com.couchbase.client.core.io.CollectionIdentifier; - /** * Represents a query method with couchbase extensions, allowing to discover if View-based query or N1QL-based query * must be used. @@ -55,13 +50,13 @@ import com.couchbase.client.core.io.CollectionIdentifier; public class CouchbaseQueryMethod extends QueryMethod { private final Method method; - private final RepositoryMetadata repositoryMetadata; public CouchbaseQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory, MappingContext, CouchbasePersistentProperty> mappingContext) { super(method, metadata, factory); + this.method = method; - this.repositoryMetadata = metadata; + } /** @@ -181,46 +176,39 @@ public class CouchbaseQueryMethod extends QueryMethod { * @return the @ScanConsistency annotation */ public ScanConsistency getScanConsistencyAnnotation() { - AnnotatedElement[] annotated = new AnnotatedElement[] { method, method.getDeclaringClass(), - repositoryMetadata.getRepositoryInterface(), repositoryMetadata.getDomainType() }; - return OptionsBuilder.annotation(ScanConsistency.class, "query", CollectionIdentifier.DEFAULT_COLLECTION, - annotated); + return method.getAnnotation(ScanConsistency.class); } /** - * Caution: findMergedAnnotation() will return the default if there are any annotations but not this annotation - * - * @return annotation + * @return return true if {@link Meta} annotation is available. */ - public
A getAnnotation(Class annotationClass) { - return AnnotatedElementUtils.findMergedAnnotation(method, annotationClass); + public boolean hasQueryMetaAttributes() { + return getMetaAnnotation() != null; } /** - * Caution: findMergedAnnotation() will return the default if there are any annotations but not this annotation - * - * @return annotation + * @return return {@link Meta} annotation */ - public A getClassAnnotation(Class annotationClass) { - return AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), annotationClass); + private Meta getMetaAnnotation() { + return method.getAnnotation(Meta.class); } /** - * Caution: findMergedAnnotation() will return the default if there are any annotations but not this annotation - * - * @return annotation + * Returns the {@link org.springframework.data.couchbase.core.query.Meta} attributes to be applied. + * + * @return never {@literal null}. */ - public A getEntityAnnotation(Class annotationClass) { - return AnnotatedElementUtils.findMergedAnnotation(getEntityInformation().getJavaType(), annotationClass); - } + @Nullable + public org.springframework.data.couchbase.core.query.Meta getQueryMetaAttributes() { - /** - * Caution: findMergedAnnotation() will return the default if there are any annotations but not this annotation - * - * @return annotation - */ - public A getRepositoryAnnotation(Class annotationClass) { - return AnnotatedElementUtils.findMergedAnnotation(repositoryMetadata.getRepositoryInterface(), annotationClass); + Meta meta = getMetaAnnotation(); + if (meta == null) { + return new org.springframework.data.couchbase.core.query.Meta(); + } + + org.springframework.data.couchbase.core.query.Meta metaAttributes = new org.springframework.data.couchbase.core.query.Meta(); + + return metaAttributes; } /** @@ -276,18 +264,4 @@ public class CouchbaseQueryMethod extends QueryMethod { return false; } - public String getCollection() { - // Try the repository method, then the repository class, then the entity class - AnnotatedElement[] annotated = new AnnotatedElement[] { method, method.getDeclaringClass(), - repositoryMetadata.getRepositoryInterface(), repositoryMetadata.getDomainType() }; - return OptionsBuilder.annotationString(Collection.class, CollectionIdentifier.DEFAULT_COLLECTION, annotated); - } - - public String getScope() { - // Try the repository method, then the repository class, then the entity class - AnnotatedElement[] annotated = new AnnotatedElement[] { method, method.getDeclaringClass(), - repositoryMetadata.getRepositoryInterface(), repositoryMetadata.getDomainType() }; - return OptionsBuilder.annotationString(Scope.class, CollectionIdentifier.DEFAULT_SCOPE, annotated); - } - } diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveStringBasedCouchbaseQuery.java b/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveStringBasedCouchbaseQuery.java index 1f29b72d..3296397a 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveStringBasedCouchbaseQuery.java +++ b/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveStringBasedCouchbaseQuery.java @@ -91,7 +91,7 @@ public class ReactiveStringBasedCouchbaseQuery extends AbstractReactiveCouchbase @Override protected Query createCountQuery(ParametersParameterAccessor accessor) { - return applyQueryMetaAttributesIfPresent(createQuery(accessor), null); + return applyQueryMetaAttributesWhenPresent(createQuery(accessor)); } /* diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedCouchbaseQuery.java b/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedCouchbaseQuery.java index 14799bd0..d52a04b3 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedCouchbaseQuery.java +++ b/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedCouchbaseQuery.java @@ -79,8 +79,8 @@ public class StringBasedCouchbaseQuery extends AbstractCouchbaseQuery { namedQueries); Query query = creator.createQuery(); - if (LOG.isTraceEnabled()) { - LOG.trace("Created query " + query.export()); + if (LOG.isDebugEnabled()) { + LOG.debug("Created query " + query.export()); } return query; @@ -88,7 +88,7 @@ public class StringBasedCouchbaseQuery extends AbstractCouchbaseQuery { @Override protected Query createCountQuery(ParametersParameterAccessor accessor) { - return applyQueryMetaAttributesIfPresent(createQuery(accessor), null); + return applyQueryMetaAttributesWhenPresent(createQuery(accessor)); } /* diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedN1qlQueryParser.java b/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedN1qlQueryParser.java index 3716ac26..bba2ef48 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedN1qlQueryParser.java +++ b/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedN1qlQueryParser.java @@ -22,6 +22,7 @@ import static org.springframework.data.couchbase.core.support.TemplateUtils.SELE import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.regex.Matcher; @@ -29,7 +30,11 @@ import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.core.convert.support.GenericConversionService; +import org.springframework.data.convert.CustomConversions; import org.springframework.data.couchbase.core.convert.CouchbaseConverter; +import org.springframework.data.couchbase.core.convert.CouchbaseCustomConversions; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty; import org.springframework.data.couchbase.core.query.N1QLExpression; import org.springframework.data.couchbase.repository.Query; @@ -126,9 +131,8 @@ public class StringBasedN1qlQueryParser { this.statement = statement; this.queryMethod = queryMethod; this.couchbaseConverter = couchbaseConverter; - String collection = queryMethod.getCollection(); - this.statementContext = createN1qlSpelValues(bucketName, collection, null, null, typeField, typeValue, false, null); - this.countContext = createN1qlSpelValues(bucketName, collection, null, null, typeField, typeValue, true, null); + this.statementContext = createN1qlSpelValues(bucketName, null, null, null, typeField, typeValue, false, null); + this.countContext = createN1qlSpelValues(bucketName, null, null, null, typeField, typeValue, true, null); this.parsedExpression = getExpression(accessor, getParameters(accessor), null, parser, evaluationContextProvider); checkPlaceholders(this.parsedExpression.toString()); } diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryBase.java b/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryBase.java deleted file mode 100644 index 992c4d0d..00000000 --- a/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryBase.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.data.couchbase.repository.support; - -import java.lang.reflect.AnnotatedElement; - -import org.springframework.data.couchbase.core.query.OptionsBuilder; -import org.springframework.data.couchbase.repository.Collection; -import org.springframework.data.couchbase.repository.ScanConsistency; -import org.springframework.data.couchbase.repository.Scope; -import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation; - -import com.couchbase.client.core.io.CollectionIdentifier; -import com.couchbase.client.java.query.QueryScanConsistency; - -public class CouchbaseRepositoryBase { - - /** - * Contains information about the entity being used in this repository. - */ - private final CouchbaseEntityInformation entityInformation; - private final Class repositoryInterface; - private CrudMethodMetadata crudMethodMetadata; - - public CouchbaseRepositoryBase(CouchbaseEntityInformation entityInformation, - Class repositoryInterface) { - this.entityInformation = entityInformation; - this.repositoryInterface = repositoryInterface; - } - - /** - * Returns the information for the underlying template. - * - * @return the underlying entity information. - */ - public CouchbaseEntityInformation getEntityInformation() { - return entityInformation; - } - - Class getJavaType() { - return getEntityInformation().getJavaType(); - } - - String getId(S entity) { - return getEntityInformation().getId(entity); - } - - /** - * Get the Scope from
- * 1. The repository
- * 2. The entity
- * 3. otherwise null
- * This can be overriden in the operation method by
- * 1. repository.withCollection() 2. Annotation on the method - */ - - String getScope() { - String fromAnnotation = OptionsBuilder.annotationString(Scope.class, CollectionIdentifier.DEFAULT_SCOPE, - new AnnotatedElement[] { getJavaType(), repositoryInterface }); - String fromMetadata = crudMethodMetadata.getScope(); - return OptionsBuilder.fromFirst(CollectionIdentifier.DEFAULT_SCOPE, fromMetadata, fromAnnotation); - } - - /** - * Get the Collection from
- * 1. The repository
- * 2. The entity
- * 3. otherwise null
- * This can be overriden in the operation method by
- * 1. repository.withCollection() - */ - String getCollection() { - String fromAnnotation = OptionsBuilder.annotationString(Collection.class, CollectionIdentifier.DEFAULT_COLLECTION, - new AnnotatedElement[] { getJavaType(), repositoryInterface }); - String fromMetadata = crudMethodMetadata.getCollection(); - return OptionsBuilder.fromFirst(CollectionIdentifier.DEFAULT_COLLECTION, fromMetadata, fromAnnotation); - } - - /** - * Get the QueryScanConsistency from
- * 1. The method annotation (method *could* be available from crudMethodMetadata)
- * 2. The repository
- * 3. The entity
- * 4. otherwise null
- * This can be overriden in the operation method by
- * 1. Options.scanConsistency (?)
- * AbstractCouchbaseQueryBase.applyAnnotatedConsistencyIfPresent()
- * CouchbaseRepository get picked up? If I have the following, will the annotation be picked up?
- * Only via crudMethodMetadata
- * \@ScanConsistency(query=QueryScanConsistency.REQUEST_PLUS)
- * List findAll();
- */ - QueryScanConsistency buildQueryScanConsistency() { - ScanConsistency sc = crudMethodMetadata.getScanConsistency(); - QueryScanConsistency fromMeta = sc != null ? sc.query() : null; - QueryScanConsistency fromAnnotation = OptionsBuilder.annotationAttribute(ScanConsistency.class, "query", - QueryScanConsistency.NOT_BOUNDED, new AnnotatedElement[] { getJavaType(), repositoryInterface }); - return OptionsBuilder.fromFirst(QueryScanConsistency.NOT_BOUNDED, fromMeta, fromAnnotation); - } - - /** - * Setter for the repository metadata, contains annotations on the overidden methods. - * - * @param crudMethodMetadata the injected repository metadata. - */ - void setRepositoryMethodMetadata(CrudMethodMetadata crudMethodMetadata) { - this.crudMethodMetadata = crudMethodMetadata; - } -} diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactory.java b/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactory.java index d8bff2d0..f42d9ff4 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactory.java +++ b/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactory.java @@ -90,14 +90,14 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport { * Returns entity information based on the domain class. * * @param domainClass the class for the entity. - * @param the value type - * @param the id type. + * @param the value type + * @param the id type. * @return entity information for that domain class. */ @Override public CouchbaseEntityInformation getEntityInformation(Class domainClass) { - CouchbasePersistentEntity entity = (CouchbasePersistentEntity) mappingContext - .getRequiredPersistentEntity(domainClass); + CouchbasePersistentEntity entity = (CouchbasePersistentEntity) mappingContext.getRequiredPersistentEntity( + domainClass); return new MappingCouchbaseEntityInformation<>(entity); } @@ -116,7 +116,7 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport { metadata.getDomainType()); CouchbaseEntityInformation entityInformation = getEntityInformation(metadata.getDomainType()); SimpleCouchbaseRepository repository = getTargetRepositoryViaReflection(metadata, entityInformation, - couchbaseOperations, metadata.getRepositoryInterface()); + couchbaseOperations); repository.setRepositoryMethodMetadata(crudMethodMetadataPostProcessor.getCrudMethodMetadata()); return repository; } @@ -153,8 +153,8 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport { @Override public RepositoryQuery resolveQuery(final Method method, final RepositoryMetadata metadata, final ProjectionFactory factory, final NamedQueries namedQueries) { - final CouchbaseOperations couchbaseOperations = couchbaseOperationsMapping - .resolve(metadata.getRepositoryInterface(), metadata.getDomainType()); + final CouchbaseOperations couchbaseOperations = couchbaseOperationsMapping.resolve( + metadata.getRepositoryInterface(), metadata.getDomainType()); CouchbaseQueryMethod queryMethod = new CouchbaseQueryMethod(method, metadata, factory, mappingContext); diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/CrudMethodMetadata.java b/src/main/java/org/springframework/data/couchbase/repository/support/CrudMethodMetadata.java index a15f17a8..710c7751 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/support/CrudMethodMetadata.java +++ b/src/main/java/org/springframework/data/couchbase/repository/support/CrudMethodMetadata.java @@ -31,8 +31,4 @@ public interface CrudMethodMetadata { */ ScanConsistency getScanConsistency(); - String getScope(); - - String getCollection(); - } diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/CrudMethodMetadataPostProcessor.java b/src/main/java/org/springframework/data/couchbase/repository/support/CrudMethodMetadataPostProcessor.java index 836db257..159a5782 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/support/CrudMethodMetadataPostProcessor.java +++ b/src/main/java/org/springframework/data/couchbase/repository/support/CrudMethodMetadataPostProcessor.java @@ -15,7 +15,7 @@ */ package org.springframework.data.couchbase.repository.support; -import java.lang.reflect.AnnotatedElement; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Set; @@ -28,10 +28,7 @@ import org.springframework.aop.TargetSource; import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.core.NamedThreadLocal; -import org.springframework.data.couchbase.core.query.OptionsBuilder; -import org.springframework.data.couchbase.repository.Collection; import org.springframework.data.couchbase.repository.ScanConsistency; -import org.springframework.data.couchbase.repository.Scope; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.support.RepositoryProxyPostProcessor; import org.springframework.lang.Nullable; @@ -40,9 +37,6 @@ import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; -import com.couchbase.client.core.io.CollectionIdentifier; -import com.couchbase.client.java.query.QueryScanConsistency; - /** * {@link RepositoryProxyPostProcessor} that sets up interceptors to read metadata information from the invoked method. * This is necessary to allow redeclaration of CRUD methods in repository interfaces and configure locking information @@ -102,10 +96,8 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B private final ConcurrentMap metadataCache = new ConcurrentHashMap<>(); private final Set implementations = new HashSet<>(); - private final RepositoryInformation repositoryInformation; CrudMethodMetadataPopulatingMethodInterceptor(RepositoryInformation repositoryInformation) { - this.repositoryInformation = repositoryInformation; ReflectionUtils.doWithMethods(repositoryInformation.getRepositoryInterface(), implementations::add, method -> !repositoryInformation.isQueryMethod(method)); } @@ -156,7 +148,7 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B if (methodMetadata == null) { - methodMetadata = new DefaultCrudMethodMetadata(method, repositoryInformation); + methodMetadata = new DefaultCrudMethodMetadata(method); CrudMethodMetadata tmp = metadataCache.putIfAbsent(method, methodMetadata); if (tmp != null) { @@ -169,10 +161,10 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B try { return invocation.proceed(); } finally { - // TransactionSynchronizationManager.unbindResource(method); + TransactionSynchronizationManager.unbindResource(method); } } finally { - // currentInvocation.set(oldInvocation); + currentInvocation.set(oldInvocation); } } } @@ -187,38 +179,23 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B private final Method method; private final ScanConsistency scanConsistency; - private final RepositoryInformation repositoryInformation; - private final String scope; - private final String collection; /** - * Creates a new {@link DefaultCrudMethodMetadata} for the given {@link Method}. This collects data from implemented - * methods (save(), findById() etc) that would be collected in query.setMeta() for unimplemented methods. There may - * be annotations if the methods were overriden in the repository. - * + * Creates a new {@link DefaultCrudMethodMetadata} for the given {@link Method}. + * * @param method must not be {@literal null}. */ - DefaultCrudMethodMetadata(Method method, RepositoryInformation repositoryInformation) { + DefaultCrudMethodMetadata(Method method) { Assert.notNull(method, "Method must not be null!"); this.method = method; - this.repositoryInformation = repositoryInformation; - String n = method.getName(); - // internal methods - if (n.equals("getEntityInformation") || n.equals("getOperations") || n.equals("withOptions") - || n.equals("withOptions") || n.equals("withScope")) { - this.scanConsistency = null; - this.scope = null; - this.collection = null; - return; - } - AnnotatedElement[] annotated = new AnnotatedElement[] { method, method.getDeclaringClass(), - repositoryInformation.getRepositoryInterface(), repositoryInformation.getDomainType() }; - this.scanConsistency = OptionsBuilder.annotation(ScanConsistency.class, "query", QueryScanConsistency.NOT_BOUNDED, - annotated); - this.scope = OptionsBuilder.annotationString(Scope.class, CollectionIdentifier.DEFAULT_SCOPE, annotated); - this.collection = OptionsBuilder.annotationString(Collection.class, CollectionIdentifier.DEFAULT_COLLECTION, - annotated); + ScanConsistency scanConsistency = null; + for (Annotation ann : method.getAnnotations()) { + if (ann instanceof ScanConsistency) { + scanConsistency = ((ScanConsistency) ann); + } + } + this.scanConsistency = scanConsistency; } /* @@ -234,17 +211,6 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B public ScanConsistency getScanConsistency() { return scanConsistency; } - - @Override - public String getScope() { - return scope; - } - - @Override - public String getCollection() { - return collection; - } - } private static class ThreadBoundTargetSource implements TargetSource { diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/DynamicInvocationHandler.java b/src/main/java/org/springframework/data/couchbase/repository/support/DynamicInvocationHandler.java deleted file mode 100644 index dedba009..00000000 --- a/src/main/java/org/springframework/data/couchbase/repository/support/DynamicInvocationHandler.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.couchbase.repository.support; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.Arrays; - -import org.springframework.data.couchbase.core.CouchbaseTemplate; -import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate; -import org.springframework.data.couchbase.core.support.PseudoArgs; -import org.springframework.data.couchbase.repository.CouchbaseRepository; -import org.springframework.data.couchbase.repository.ReactiveCouchbaseRepository; -import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation; - -import com.couchbase.client.java.CommonOptions; - -/** - * Invocation Handler for scope/collection/options proxy for repositories - * - * @param - * @author Michael Reiche - */ -public class DynamicInvocationHandler implements InvocationHandler { - final T target; - final Class repositoryClass; - // needed only to detect parameters of this type to look for methods with parameter of java.lang.Object - final CouchbaseEntityInformation entityInformation; - final ReactiveCouchbaseTemplate reactiveTemplate; - CommonOptions options; - String collection; - String scope;; - - public DynamicInvocationHandler(T target, CommonOptions options, String collection, String scope) { - this.target = target; - if (target instanceof CouchbaseRepository) { - reactiveTemplate = ((CouchbaseTemplate) ((CouchbaseRepository) target).getOperations()).reactive(); - this.entityInformation = ((CouchbaseRepository) target).getEntityInformation(); - } else if (target instanceof ReactiveCouchbaseRepository) { - reactiveTemplate = (ReactiveCouchbaseTemplate) ((ReactiveCouchbaseRepository) target).getOperations(); - this.entityInformation = ((ReactiveCouchbaseRepository) target).getEntityInformation(); - } else { - throw new RuntimeException("Unknown target type: " + target.getClass()); - } - this.options = options; - this.collection = collection; - this.scope = scope; - this.repositoryClass = target.getClass(); - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - - if ("toString".equals(method.getName())) { - return "proxy -> target:" + target; - } - /* Cannot fall-through to use these methods on target, as they will not retain - * the scope, collection and options that may already be set on the proxy - */ - - if (method.getName().equals("withOptions")) { - return Proxy.newProxyInstance(repositoryClass.getClassLoader(), target.getClass().getInterfaces(), - new DynamicInvocationHandler<>(target, (CommonOptions) args[0], collection, scope)); - } - - if (method.getName().equals("withScope")) { - return Proxy.newProxyInstance(repositoryClass.getClassLoader(), target.getClass().getInterfaces(), - new DynamicInvocationHandler<>(target, options, collection, (String) args[0])); - } - - if (method.getName().equals("withCollection")) { - return Proxy.newProxyInstance(repositoryClass.getClassLoader(), target.getClass().getInterfaces(), - new DynamicInvocationHandler<>(target, options, (String) args[0], scope)); - } - - Class[] paramTypes = null; - if (args != null) { - // the CouchbaseRepository methods - save(entity) etc - will have a parameter type of Object instead of entityType - // so change the paramType to match - paramTypes = Arrays.stream(args) - .map(o -> o == null ? null : (o.getClass() == entityInformation.getJavaType() ? Object.class : o.getClass())) - .toArray(Class[]::new); - // the CouchbaseRepository methods - findById(id) etc - will have a parameter type of Object instead of ID - if (method.getName().endsWith("ById") && args.length == 1) { - paramTypes[0] = Object.class; - } - } - - Method theMethod = repositoryClass.getMethod(method.getName(), paramTypes); - Object result; - - try { - setThreadLocal(); - result = theMethod.invoke(target, args); - } catch (InvocationTargetException ite) { - throw ite.getCause(); - } - return result; - } - - private void setThreadLocal() { - if (reactiveTemplate.getPseudoArgs() != null) { - throw new RuntimeException("pseudoArgs not yet consumed by previous caller"); - } - reactiveTemplate.setPseudoArgs(new PseudoArgs(this.scope, this.collection, this.options)); - } - -} diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/ReactiveCouchbaseRepositoryFactory.java b/src/main/java/org/springframework/data/couchbase/repository/support/ReactiveCouchbaseRepositoryFactory.java index 273fb656..8ca423c3 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/support/ReactiveCouchbaseRepositoryFactory.java +++ b/src/main/java/org/springframework/data/couchbase/repository/support/ReactiveCouchbaseRepositoryFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -66,7 +66,7 @@ public class ReactiveCouchbaseRepositoryFactory extends ReactiveRepositoryFactor * @param couchbaseOperationsMapping the template for the underlying actions. */ public ReactiveCouchbaseRepositoryFactory(final ReactiveRepositoryOperationsMapping couchbaseOperationsMapping) { - Assert.notNull(couchbaseOperationsMapping, "couchbaseOperationsMapping"); + Assert.notNull(couchbaseOperationsMapping); this.couchbaseOperationsMapping = couchbaseOperationsMapping; this.crudMethodMetadataPostProcessor = new CrudMethodMetadataPostProcessor(); @@ -85,14 +85,14 @@ public class ReactiveCouchbaseRepositoryFactory extends ReactiveRepositoryFactor * Returns entity information based on the domain class. * * @param domainClass the class for the entity. - * @param the value type - * @param the id type. + * @param the value type + * @param the id type. * @return entity information for that domain class. */ @Override public CouchbaseEntityInformation getEntityInformation(Class domainClass) { - CouchbasePersistentEntity entity = (CouchbasePersistentEntity) mappingContext - .getRequiredPersistentEntity(domainClass); + CouchbasePersistentEntity entity = (CouchbasePersistentEntity) mappingContext.getRequiredPersistentEntity( + domainClass); return new MappingCouchbaseEntityInformation<>(entity); } @@ -107,11 +107,11 @@ public class ReactiveCouchbaseRepositoryFactory extends ReactiveRepositoryFactor */ @Override protected final Object getTargetRepository(final RepositoryInformation metadata) { - ReactiveCouchbaseOperations couchbaseOperations = couchbaseOperationsMapping - .resolve(metadata.getRepositoryInterface(), metadata.getDomainType()); + ReactiveCouchbaseOperations couchbaseOperations = couchbaseOperationsMapping.resolve( + metadata.getRepositoryInterface(), metadata.getDomainType()); CouchbaseEntityInformation entityInformation = getEntityInformation(metadata.getDomainType()); SimpleReactiveCouchbaseRepository repository = getTargetRepositoryViaReflection(metadata, entityInformation, - couchbaseOperations, metadata.getRepositoryInterface()); + couchbaseOperations); repository.setRepositoryMethodMetadata(crudMethodMetadataPostProcessor.getCrudMethodMetadata()); return repository; } @@ -150,8 +150,8 @@ public class ReactiveCouchbaseRepositoryFactory extends ReactiveRepositoryFactor @Override public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory, NamedQueries namedQueries) { - final ReactiveCouchbaseOperations couchbaseOperations = couchbaseOperationsMapping - .resolve(metadata.getRepositoryInterface(), metadata.getDomainType()); + final ReactiveCouchbaseOperations couchbaseOperations = couchbaseOperationsMapping.resolve( + metadata.getRepositoryInterface(), metadata.getDomainType()); ReactiveCouchbaseQueryMethod queryMethod = new ReactiveCouchbaseQueryMethod(method, metadata, factory, mappingContext); diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java b/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java index e2cc0338..c91235d4 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java +++ b/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,27 +44,34 @@ import com.couchbase.client.java.query.QueryScanConsistency; * @author Michael Nitschinger * @author Mark Paluch * @author Jens Schauder - * @author Michael Reiche */ -public class SimpleCouchbaseRepository extends CouchbaseRepositoryBase - implements CouchbaseRepository { +public class SimpleCouchbaseRepository implements CouchbaseRepository { /** * Holds the reference to the {@link org.springframework.data.couchbase.core.CouchbaseTemplate}. */ - private final CouchbaseOperations operations; + private final CouchbaseOperations couchbaseOperations; + + /** + * Contains information about the entity being used in this repository. + */ + private final CouchbaseEntityInformation entityInformation; + + private CrudMethodMetadata crudMethodMetadata; /** * Create a new Repository. * * @param entityInformation the Metadata for the entity. * @param couchbaseOperations the reference to the template used. - * @param repositoryInterface the repository interface being fronted */ public SimpleCouchbaseRepository(CouchbaseEntityInformation entityInformation, - CouchbaseOperations couchbaseOperations, Class repositoryInterface) { - super(entityInformation, repositoryInterface); - this.operations = couchbaseOperations; + CouchbaseOperations couchbaseOperations) { + Assert.notNull(entityInformation, "CouchbaseEntityInformation must not be null!"); + Assert.notNull(couchbaseOperations, "CouchbaseOperations must not be null!"); + + this.entityInformation = entityInformation; + this.couchbaseOperations = couchbaseOperations; } @Override @@ -72,13 +79,11 @@ public class SimpleCouchbaseRepository extends CouchbaseRepositoryBase S save(S entity) { Assert.notNull(entity, "Entity must not be null!"); // if entity has non-null, non-zero version property, then replace() - S result; - if (hasNonZeroVersionProperty(entity, operations.getConverter())) { - result = (S) operations.replaceById(getJavaType()).inScope(getScope()).inCollection(getCollection()).one(entity); + if (hasNonZeroVersionProperty(entity, couchbaseOperations.getConverter())) { + return (S) couchbaseOperations.replaceById(entityInformation.getJavaType()).one(entity); } else { - result = (S) operations.upsertById(getJavaType()).inScope(getScope()).inCollection(getCollection()).one(entity); + return (S) couchbaseOperations.upsertById(entityInformation.getJavaType()).one(entity); } - return result; } @Override @@ -90,61 +95,57 @@ public class SimpleCouchbaseRepository extends CouchbaseRepositoryBase findById(ID id) { Assert.notNull(id, "The given id must not be null!"); - return Optional.ofNullable( - operations.findById(getJavaType()).inScope(getScope()).inCollection(getCollection()).one(id.toString())); + return Optional.ofNullable(couchbaseOperations.findById(entityInformation.getJavaType()).one(id.toString())); } @Override public List findAllById(Iterable ids) { Assert.notNull(ids, "The given Iterable of ids must not be null!"); List convertedIds = Streamable.of(ids).stream().map(Objects::toString).collect(Collectors.toList()); - Collection all = operations.findById(getJavaType()).inScope(getScope()).inCollection(getCollection()) - .all(convertedIds); + Collection all = couchbaseOperations.findById(entityInformation.getJavaType()).all(convertedIds); return Streamable.of(all).stream().collect(StreamUtils.toUnmodifiableList()); } @Override public boolean existsById(ID id) { Assert.notNull(id, "The given id must not be null!"); - return operations.existsById(getJavaType()).inScope(getScope()).inCollection(getCollection()).one(id.toString()); + return couchbaseOperations.existsById().one(id.toString()); } @Override public void deleteById(ID id) { Assert.notNull(id, "The given id must not be null!"); - operations.removeById(getJavaType()).inScope(getScope()).inCollection(getCollection()).one(id.toString()); + couchbaseOperations.removeById().one(id.toString()); } @Override public void delete(T entity) { Assert.notNull(entity, "Entity must not be null!"); - operations.removeById(getJavaType()).inScope(getScope()).inCollection(getCollection()).one(getId(entity)); + couchbaseOperations.removeById().one(entityInformation.getId(entity)); } @Override public void deleteAllById(Iterable ids) { Assert.notNull(ids, "The given Iterable of ids must not be null!"); - operations.removeById(getJavaType()).inScope(getScope()).inCollection(getCollection()) - .all(Streamable.of(ids).map(Objects::toString).toList()); + couchbaseOperations.removeById().all(Streamable.of(ids).map(Objects::toString).toList()); } @Override public void deleteAll(Iterable entities) { Assert.notNull(entities, "The given Iterable of entities must not be null!"); - operations.removeById(getJavaType()).inScope(getScope()).inCollection(getCollection()) - .all(Streamable.of(entities).map(this::getId).toList()); + couchbaseOperations.removeById().all(Streamable.of(entities).map(entityInformation::getId).toList()); } @Override public long count() { - return operations.findByQuery(getJavaType()).withConsistency(buildQueryScanConsistency()).inScope(getScope()) - .inCollection(getCollection()).count(); + return couchbaseOperations.findByQuery(entityInformation.getJavaType()).withConsistency(buildQueryScanConsistency()) + .count(); } @Override public void deleteAll() { - operations.removeByQuery(getJavaType()).withConsistency(buildQueryScanConsistency()).inScope(getScope()) - .inCollection(getCollection()).all(); + couchbaseOperations.removeByQuery(entityInformation.getJavaType()).withConsistency(buildQueryScanConsistency()) + .all(); } @Override @@ -168,6 +169,15 @@ public class SimpleCouchbaseRepository extends CouchbaseRepositoryBase(results, pageable, count()); } + /** + * Returns the information for the underlying template. + * + * @return the underlying entity information. + */ + protected CouchbaseEntityInformation getEntityInformation() { + return entityInformation; + } + /** * Helper method to assemble a n1ql find all query, taking annotations into acocunt. * @@ -175,13 +185,25 @@ public class SimpleCouchbaseRepository extends CouchbaseRepositoryBase findAll(Query query) { - return operations.findByQuery(getJavaType()).withConsistency(buildQueryScanConsistency()).inScope(getScope()) - .inCollection(getCollection()).matching(query).all(); + return couchbaseOperations.findByQuery(entityInformation.getJavaType()).withConsistency(buildQueryScanConsistency()) + .matching(query).all(); } - @Override - public CouchbaseOperations getOperations() { - return operations; + private QueryScanConsistency buildQueryScanConsistency() { + QueryScanConsistency scanConsistency = QueryScanConsistency.NOT_BOUNDED; + if (crudMethodMetadata.getScanConsistency() != null) { + scanConsistency = crudMethodMetadata.getScanConsistency().query(); + } + return scanConsistency; + } + + /** + * Setter for the repository metadata, contains annotations on the overidden methods. + * + * @param crudMethodMetadata the injected repository metadata. + */ + void setRepositoryMethodMetadata(CrudMethodMetadata crudMethodMetadata) { + this.crudMethodMetadata = crudMethodMetadata; } } diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/SimpleReactiveCouchbaseRepository.java b/src/main/java/org/springframework/data/couchbase/repository/support/SimpleReactiveCouchbaseRepository.java index 2aa7ac6e..7cf8320e 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/support/SimpleReactiveCouchbaseRepository.java +++ b/src/main/java/org/springframework/data/couchbase/repository/support/SimpleReactiveCouchbaseRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,8 @@ import org.springframework.data.domain.Sort; import org.springframework.data.util.Streamable; import org.springframework.util.Assert; +import com.couchbase.client.java.query.QueryScanConsistency; + /** * Reactive repository base implementation for Couchbase. * @@ -44,17 +46,22 @@ import org.springframework.util.Assert; * @author David Kelly * @author Douglas Six * @author Jens Schauder - * @author Michael Reiche * @since 3.0 */ -public class SimpleReactiveCouchbaseRepository extends CouchbaseRepositoryBase - implements ReactiveCouchbaseRepository { +public class SimpleReactiveCouchbaseRepository implements ReactiveCouchbaseRepository { /** * Holds the reference to the {@link CouchbaseOperations}. */ private final ReactiveCouchbaseOperations operations; + /** + * Contains information about the entity being used in this repository. + */ + private final CouchbaseEntityInformation entityInformation; + + private CrudMethodMetadata crudMethodMetadata; + /** * Create a new Repository. * @@ -62,8 +69,11 @@ public class SimpleReactiveCouchbaseRepository extends CouchbaseRepositor * @param operations the reference to the reactive template used. */ public SimpleReactiveCouchbaseRepository(CouchbaseEntityInformation entityInformation, - ReactiveCouchbaseOperations operations, Class repositoryInterface) { - super(entityInformation, repositoryInterface); + ReactiveCouchbaseOperations operations) { + Assert.notNull(operations, "ReactiveCouchbaseOperations must not be null!"); + Assert.notNull(entityInformation, "CouchbaseEntityInformation must not be null!"); + + this.entityInformation = entityInformation; this.operations = operations; } @@ -71,16 +81,12 @@ public class SimpleReactiveCouchbaseRepository extends CouchbaseRepositor @Override public Mono save(S entity) { Assert.notNull(entity, "Entity must not be null!"); - // if entity has non-null, non-zero version property, then replace() - Mono result; + // if entity has non-null version property, then replace() if (hasNonZeroVersionProperty(entity, operations.getConverter())) { - result = (Mono) operations.replaceById(getJavaType()).inScope(getScope()).inCollection(getCollection()) - .one(entity); + return (Mono) operations.replaceById(entityInformation.getJavaType()).one(entity); } else { - result = (Mono) operations.upsertById(getJavaType()).inScope(getScope()).inCollection(getCollection()) - .one(entity); + return (Mono) operations.upsertById(entityInformation.getJavaType()).one(entity); } - return result; } @Override @@ -91,7 +97,7 @@ public class SimpleReactiveCouchbaseRepository extends CouchbaseRepositor @Override public Flux saveAll(Iterable entities) { Assert.notNull(entities, "The given Iterable of entities must not be null!"); - return Flux.fromIterable(entities).flatMap(e -> save(e)); + return Flux.fromIterable(entities).flatMap(this::save); } @Override @@ -102,7 +108,7 @@ public class SimpleReactiveCouchbaseRepository extends CouchbaseRepositor @Override public Mono findById(ID id) { - return operations.findById(getJavaType()).inScope(getScope()).inCollection(getCollection()).one(id.toString()); + return operations.findById(entityInformation.getJavaType()).one(id.toString()); } @Override @@ -114,7 +120,7 @@ public class SimpleReactiveCouchbaseRepository extends CouchbaseRepositor @Override public Mono existsById(ID id) { Assert.notNull(id, "The given id must not be null!"); - return operations.existsById(getJavaType()).inScope(getScope()).inCollection(getCollection()).one(id.toString()); + return operations.existsById().one(id.toString()); } @Override @@ -133,8 +139,7 @@ public class SimpleReactiveCouchbaseRepository extends CouchbaseRepositor public Flux findAllById(Iterable ids) { Assert.notNull(ids, "The given Iterable of ids must not be null!"); List convertedIds = Streamable.of(ids).stream().map(Objects::toString).collect(Collectors.toList()); - return (Flux) operations.findById(getJavaType()).inScope(getScope()).inCollection(getCollection()) - .all(convertedIds); + return (Flux) operations.findById(entityInformation.getJavaType()).all(convertedIds); } @Override @@ -145,8 +150,7 @@ public class SimpleReactiveCouchbaseRepository extends CouchbaseRepositor @Override public Mono deleteById(ID id) { - return operations.removeById(getJavaType()).inScope(getScope()).inCollection(getCollection()).one(id.toString()) - .then(); + return operations.removeById().one(id.toString()).then(); } @Override @@ -158,20 +162,17 @@ public class SimpleReactiveCouchbaseRepository extends CouchbaseRepositor @Override public Mono delete(T entity) { Assert.notNull(entity, "Entity must not be null!"); - return operations.removeById(getJavaType()).inScope(getScope()).inCollection(getCollection()).one(getId(entity)) - .then(); + return operations.removeById().one(entityInformation.getId(entity)).then(); } @Override public Mono deleteAllById(Iterable ids) { - return operations.removeById(getJavaType()).inScope(getScope()).inCollection(getCollection()) - .all(Streamable.of(ids).map(Object::toString).toList()).then(); + return operations.removeById().all(Streamable.of(ids).map(Object::toString).toList()).then(); } @Override public Mono deleteAll(Iterable entities) { - return operations.removeById(getJavaType()).inScope(getScope()).inCollection(getCollection()) - .all(Streamable.of(entities).map(this::getId).toList()).then(); + return operations.removeById().all(Streamable.of(entities).map(entityInformation::getId).toList()).then(); } @Override @@ -182,24 +183,43 @@ public class SimpleReactiveCouchbaseRepository extends CouchbaseRepositor @Override public Mono count() { - return operations.findByQuery(getJavaType()).withConsistency(buildQueryScanConsistency()).inScope(getScope()) - .inCollection(getCollection()).count(); + return operations.findByQuery(entityInformation.getJavaType()).withConsistency(buildQueryScanConsistency()).count(); } @Override public Mono deleteAll() { - return operations.removeByQuery(getJavaType()).withConsistency(buildQueryScanConsistency()).inScope(getScope()) - .inCollection(getCollection()).all().then(); + return operations.removeByQuery(entityInformation.getJavaType()).withConsistency(buildQueryScanConsistency()).all().then(); + } + + /** + * Returns the information for the underlying template. + * + * @return the underlying entity information. + */ + protected CouchbaseEntityInformation getEntityInformation() { + return entityInformation; } private Flux findAll(Query query) { - return operations.findByQuery(getJavaType()).withConsistency(buildQueryScanConsistency()).inScope(getScope()) - .inCollection(getCollection()).matching(query).all(); + return operations.findByQuery(entityInformation.getJavaType()).withConsistency(buildQueryScanConsistency()) + .matching(query).all(); } - @Override - public ReactiveCouchbaseOperations getOperations() { - return operations; + private QueryScanConsistency buildQueryScanConsistency() { + QueryScanConsistency scanConsistency = QueryScanConsistency.NOT_BOUNDED; + if (crudMethodMetadata.getScanConsistency() != null) { + scanConsistency = crudMethodMetadata.getScanConsistency().query(); + } + return scanConsistency; + } + + /** + * Setter for the repository metadata, contains annotations on the overidden methods. + * + * @param crudMethodMetadata the injected repository metadata. + */ + void setRepositoryMethodMetadata(CrudMethodMetadata crudMethodMetadata) { + this.crudMethodMetadata = crudMethodMetadata; } } diff --git a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java index f98cbf7e..c0980105 100644 --- a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java @@ -31,6 +31,7 @@ import java.util.HashSet; import java.util.Set; import java.util.UUID; +import com.couchbase.client.java.query.QueryScanConsistency; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.dao.DataIntegrityViolationException; @@ -49,7 +50,6 @@ import org.springframework.data.couchbase.util.JavaIntegrationTests; import com.couchbase.client.java.kv.PersistTo; import com.couchbase.client.java.kv.ReplicateTo; -import com.couchbase.client.java.query.QueryScanConsistency; ; @@ -121,7 +121,7 @@ class CouchbaseTemplateKeyValueIntegrationTests extends JavaIntegrationTests { returned = (User) operator.one(user); break; } catch (Exception ofe) { - System.out.println("" + i + " caught: " + ofe); + System.out.println(""+i+" caught: "+ofe); couchbaseTemplate.removeByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all(); if (i == 4) { throw ofe; @@ -259,7 +259,7 @@ class CouchbaseTemplateKeyValueIntegrationTests extends JavaIntegrationTests { .one(user); break; } catch (Exception ofe) { - System.out.println("" + i + " caught: " + ofe); + System.out.println(""+i+" caught: "+ofe); couchbaseTemplate.removeByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all(); if (i == 4) { throw ofe; diff --git a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryCollectionIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryCollectionIntegrationTests.java index 586986af..3a56b8ff 100644 --- a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryCollectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryCollectionIntegrationTests.java @@ -36,7 +36,6 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.springframework.dao.DataRetrievalFailureException; import org.springframework.data.couchbase.core.query.Query; import org.springframework.data.couchbase.core.query.QueryCriteria; import org.springframework.data.couchbase.domain.Address; @@ -45,19 +44,18 @@ import org.springframework.data.couchbase.domain.Course; import org.springframework.data.couchbase.domain.NaiveAuditorAware; import org.springframework.data.couchbase.domain.Submission; import org.springframework.data.couchbase.domain.User; -import org.springframework.data.couchbase.domain.UserCol; import org.springframework.data.couchbase.domain.UserJustLastName; import org.springframework.data.couchbase.domain.UserSubmission; import org.springframework.data.couchbase.domain.UserSubmissionProjected; import org.springframework.data.couchbase.domain.time.AuditingDateTimeProvider; import org.springframework.data.couchbase.util.Capabilities; +import org.springframework.data.couchbase.util.ClusterAwareIntegrationTests; import org.springframework.data.couchbase.util.ClusterType; import org.springframework.data.couchbase.util.CollectionAwareIntegrationTests; import org.springframework.data.couchbase.util.IgnoreWhen; import com.couchbase.client.core.error.AmbiguousTimeoutException; import com.couchbase.client.core.error.UnambiguousTimeoutException; -import com.couchbase.client.core.io.CollectionIdentifier; import com.couchbase.client.java.analytics.AnalyticsOptions; import com.couchbase.client.java.kv.ExistsOptions; import com.couchbase.client.java.kv.GetAnyReplicaOptions; @@ -754,51 +752,4 @@ class CouchbaseTemplateQueryCollectionIntegrationTests extends CollectionAwareIn .inCollection(otherCollection).withOptions(options).one(vie)); } - @Test - public void testScopeCollectionAnnotation() { - UserCol user = new UserCol("1", "Dave", "Wilson"); - Query query = Query.query(QueryCriteria.where("firstname").is(user.getFirstname())); - try { - UserCol saved = couchbaseTemplate.insertById(UserCol.class).inScope(scopeName).inCollection(collectionName) - .one(user); - List found = couchbaseTemplate.findByQuery(UserCol.class) - .withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(scopeName).inCollection(collectionName) - .matching(query).all(); - assertEquals(saved, found.get(0), "should have found what was saved"); - List notfound = couchbaseTemplate.findByQuery(UserCol.class).inScope(CollectionIdentifier.DEFAULT_SCOPE) - .inCollection(CollectionIdentifier.DEFAULT_COLLECTION).matching(query).all(); - assertEquals(0, notfound.size(), "should not have found what was saved"); - couchbaseTemplate.removeByQuery(UserCol.class).inScope(scopeName).inCollection(collectionName).matching(query) - .all(); - } finally { - try { - couchbaseTemplate.removeByQuery(UserCol.class).inScope(scopeName).inCollection(collectionName).matching(query) - .all(); - } catch (DataRetrievalFailureException drfe) {} - } - } - - @Test - public void testScopeCollectionRepoWith() { - UserCol user = new UserCol("1", "Dave", "Wilson"); - Query query = Query.query(QueryCriteria.where("firstname").is(user.getFirstname())); - try { - UserCol saved = couchbaseTemplate.insertById(UserCol.class).inScope(scopeName).inCollection(collectionName) - .one(user); - List found = couchbaseTemplate.findByQuery(UserCol.class) - .withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(scopeName).inCollection(collectionName) - .matching(query).all(); - assertEquals(saved, found.get(0), "should have found what was saved"); - List notfound = couchbaseTemplate.findByQuery(UserCol.class).inScope(CollectionIdentifier.DEFAULT_SCOPE) - .inCollection(CollectionIdentifier.DEFAULT_COLLECTION).matching(query).all(); - assertEquals(0, notfound.size(), "should not have found what was saved"); - couchbaseTemplate.removeByQuery(UserCol.class).inScope(scopeName).inCollection(collectionName).matching(query) - .all(); - } finally { - try { - couchbaseTemplate.removeByQuery(UserCol.class).inScope(scopeName).inCollection(collectionName).matching(query) - .all(); - } catch (DataRetrievalFailureException drfe) {} - } - } } diff --git a/src/test/java/org/springframework/data/couchbase/core/CustomTypeKeyIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/core/CustomTypeKeyIntegrationTests.java index 40adb9c1..655c4a32 100644 --- a/src/test/java/org/springframework/data/couchbase/core/CustomTypeKeyIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/CustomTypeKeyIntegrationTests.java @@ -16,8 +16,7 @@ package org.springframework.data.couchbase.core; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.*; import java.time.Duration; import java.util.UUID; diff --git a/src/test/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateKeyValueIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateKeyValueIntegrationTests.java index 4fc5e998..d4a47971 100644 --- a/src/test/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateKeyValueIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateKeyValueIntegrationTests.java @@ -150,8 +150,7 @@ class ReactiveCouchbaseTemplateKeyValueIntegrationTests extends JavaIntegrationT } // if replace or remove, we need to insert a document to replace - if (operator instanceof ReactiveReplaceByIdOperation.ReactiveReplaceById - || operator instanceof ExecutableRemoveById) { + if (operator instanceof ReactiveReplaceByIdOperation.ReactiveReplaceById || operator instanceof ExecutableRemoveById) { reactiveCouchbaseTemplate.insertById(User.class).one(user).block(); } // call to insert/replace/update diff --git a/src/test/java/org/springframework/data/couchbase/domain/Airport.java b/src/test/java/org/springframework/data/couchbase/domain/Airport.java index bb7ecae2..e73a8180 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/Airport.java +++ b/src/test/java/org/springframework/data/couchbase/domain/Airport.java @@ -19,8 +19,8 @@ package org.springframework.data.couchbase.domain; import org.springframework.data.annotation.CreatedBy; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.PersistenceConstructor; -import org.springframework.data.annotation.TypeAlias; import org.springframework.data.annotation.Version; +import org.springframework.data.annotation.TypeAlias; import org.springframework.data.couchbase.core.mapping.Document; /** @@ -42,6 +42,7 @@ public class Airport extends ComparableEntity { @CreatedBy private String createdBy; + @PersistenceConstructor public Airport(String id, String iata, String icao) { this.id = id; @@ -77,7 +78,6 @@ public class Airport extends ComparableEntity { version = Long.valueOf(0); return this; } - public String getCreatedBy() { return createdBy; } diff --git a/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java b/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java index 589c55ce..d849d32e 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java +++ b/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java @@ -16,33 +16,18 @@ package org.springframework.data.couchbase.domain; -import static com.couchbase.client.core.io.CollectionIdentifier.DEFAULT_COLLECTION; -import static com.couchbase.client.core.io.CollectionIdentifier.DEFAULT_SCOPE; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; import java.util.List; import java.util.Optional; -import java.util.concurrent.TimeUnit; -import org.springframework.core.annotation.AliasFor; import org.springframework.data.couchbase.core.RemoveResult; -import org.springframework.data.couchbase.core.mapping.Expiry; -import org.springframework.data.couchbase.repository.Collection; import org.springframework.data.couchbase.repository.CouchbaseRepository; -import org.springframework.data.couchbase.repository.DynamicProxyable; -import org.springframework.data.couchbase.repository.Options; import org.springframework.data.couchbase.repository.Query; import org.springframework.data.couchbase.repository.ScanConsistency; -import org.springframework.data.couchbase.repository.Scope; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; -import com.couchbase.client.java.analytics.AnalyticsScanConsistency; import com.couchbase.client.java.query.QueryScanConsistency; /** @@ -56,11 +41,8 @@ import com.couchbase.client.java.query.QueryScanConsistency; * @author Michael Reiche */ @Repository -// @Scope("repositoryScope") -// @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) -public interface AirportRepository extends CouchbaseRepository, DynamicProxyable { +public interface AirportRepository extends CouchbaseRepository { - // override an annotate with REQUEST_PLUS @Override @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) List findAll(); @@ -69,16 +51,11 @@ public interface AirportRepository extends CouchbaseRepository, List findAllByIata(String iata); @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) - @ComposedMetaAnnotation(collection = "_default", timeoutMs = 1000) Airport findByIata(String iata); @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) Airport findByIata(Iata iata); - // NOT_BOUNDED to test ScanConsistency - // @ScanConsistency(query = QueryScanConsistency.NOT_BOUNDED) - Airport iata(String iata); - @Query("#{#n1ql.selectEntity} where iata = $1") @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) List getAllByIata(String iata); @@ -120,42 +97,4 @@ public interface AirportRepository extends CouchbaseRepository, @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) Optional findByIdAndIata(String id, String iata); - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.METHOD, ElementType.TYPE }) - // @Meta - @Scope - @Collection - @ScanConsistency - @Expiry - @Options - public @interface ComposedMetaAnnotation { - - // @AliasFor(annotation = Meta.class, attribute = "maxExecutionTimeMs") - // long execTime() default -1; - - @AliasFor(annotation = ScanConsistency.class, attribute = "query") - QueryScanConsistency query() default QueryScanConsistency.NOT_BOUNDED; - - @AliasFor(annotation = ScanConsistency.class, attribute = "analytics") - AnalyticsScanConsistency analytics() default AnalyticsScanConsistency.NOT_BOUNDED; - - @AliasFor(annotation = Scope.class, attribute = "value") - String scope() default DEFAULT_SCOPE; - - @AliasFor(annotation = Collection.class, attribute = "value") - String collection() default DEFAULT_COLLECTION; - - @AliasFor(annotation = Expiry.class, attribute = "expiry") - int expiry() default 0; - - @AliasFor(annotation = Expiry.class, attribute = "expiryUnit") - TimeUnit expiryUnit() default TimeUnit.SECONDS; - - @AliasFor(annotation = Expiry.class, attribute = "expiryExpression") - String expiryExpression() default ""; - - @AliasFor(annotation = Options.class, attribute = "timeoutMs") - long timeoutMs() default 0; - - } } diff --git a/src/test/java/org/springframework/data/couchbase/domain/Config.java b/src/test/java/org/springframework/data/couchbase/domain/Config.java index 78b78077..4e2bc5c4 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/Config.java +++ b/src/test/java/org/springframework/data/couchbase/domain/Config.java @@ -38,7 +38,6 @@ import org.springframework.data.couchbase.domain.time.AuditingDateTimeProvider; import org.springframework.data.couchbase.repository.auditing.EnableCouchbaseAuditing; import org.springframework.data.couchbase.repository.auditing.EnableReactiveCouchbaseAuditing; import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories; -import org.springframework.data.couchbase.repository.config.EnableReactiveCouchbaseRepositories; import org.springframework.data.couchbase.repository.config.ReactiveRepositoryOperationsMapping; import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping; @@ -53,10 +52,8 @@ import com.couchbase.client.java.json.JacksonTransformers; */ @Configuration @EnableCouchbaseRepositories -@EnableReactiveCouchbaseRepositories -@EnableCouchbaseAuditing(auditorAwareRef = "auditorAwareRef", dateTimeProviderRef = "dateTimeProviderRef") -@EnableReactiveCouchbaseAuditing(auditorAwareRef = "reactiveAuditorAwareRef", - dateTimeProviderRef = "dateTimeProviderRef") +@EnableCouchbaseAuditing(auditorAwareRef="auditorAwareRef", dateTimeProviderRef="dateTimeProviderRef") // this activates auditing +@EnableReactiveCouchbaseAuditing(auditorAwareRef="reactiveAuditorAwareRef", dateTimeProviderRef="dateTimeProviderRef") // this activates auditing public class Config extends AbstractCouchbaseConfiguration { String bucketname = "travel-sample"; diff --git a/src/test/java/org/springframework/data/couchbase/domain/PersonRepository.java b/src/test/java/org/springframework/data/couchbase/domain/PersonRepository.java index 23e1943d..4fdcad52 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/PersonRepository.java +++ b/src/test/java/org/springframework/data/couchbase/domain/PersonRepository.java @@ -16,6 +16,7 @@ package org.springframework.data.couchbase.domain; import java.util.List; +import java.util.Optional; import java.util.UUID; import org.springframework.data.couchbase.repository.Query; @@ -91,7 +92,7 @@ public interface PersonRepository extends CrudRepository { Iterable saveAll(Iterable var1); - Person findById(UUID var1); + Optional findById(UUID var1); boolean existsById(UUID var1); diff --git a/src/test/java/org/springframework/data/couchbase/domain/ReactiveAirportRepository.java b/src/test/java/org/springframework/data/couchbase/domain/ReactiveAirportRepository.java index 59426ec2..1c517590 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/ReactiveAirportRepository.java +++ b/src/test/java/org/springframework/data/couchbase/domain/ReactiveAirportRepository.java @@ -16,19 +16,19 @@ package org.springframework.data.couchbase.domain; +import org.springframework.data.couchbase.core.RemoveResult; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.ArrayList; -import org.springframework.data.couchbase.repository.DynamicProxyable; import org.springframework.data.couchbase.repository.Query; -import org.springframework.data.couchbase.repository.ReactiveCouchbaseRepository; import org.springframework.data.couchbase.repository.ScanConsistency; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.repository.reactive.ReactiveSortingRepository; import org.springframework.stereotype.Repository; import com.couchbase.client.java.json.JsonArray; @@ -41,8 +41,7 @@ import com.couchbase.client.java.query.QueryScanConsistency; * @author Michael Reiche */ @Repository -public interface ReactiveAirportRepository - extends ReactiveCouchbaseRepository, DynamicProxyable { +public interface ReactiveAirportRepository extends ReactiveSortingRepository { @Override @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) @@ -58,9 +57,6 @@ public interface ReactiveAirportRepository @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) Flux findAllByIata(String iata); - @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) - Mono iata(String iata); - @Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter}") Flux findAllPoliciesByApplicableTypes(String state, JsonArray applicableTypes); diff --git a/src/test/java/org/springframework/data/couchbase/domain/ReactiveNaiveAuditorAware.java b/src/test/java/org/springframework/data/couchbase/domain/ReactiveNaiveAuditorAware.java index bcdf2677..0f3b0814 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/ReactiveNaiveAuditorAware.java +++ b/src/test/java/org/springframework/data/couchbase/domain/ReactiveNaiveAuditorAware.java @@ -15,9 +15,9 @@ */ package org.springframework.data.couchbase.domain; +import org.springframework.data.domain.ReactiveAuditorAware; import reactor.core.publisher.Mono; -import org.springframework.data.domain.ReactiveAuditorAware; /** * This class returns a string that represents the current user @@ -28,7 +28,6 @@ import org.springframework.data.domain.ReactiveAuditorAware; public class ReactiveNaiveAuditorAware implements ReactiveAuditorAware { public static final String AUDITOR = "reactive_auditor"; - @Override public Mono getCurrentAuditor() { return Mono.just(AUDITOR); diff --git a/src/test/java/org/springframework/data/couchbase/domain/ReactiveUserColRepository.java b/src/test/java/org/springframework/data/couchbase/domain/ReactiveUserColRepository.java deleted file mode 100644 index 0b5c742e..00000000 --- a/src/test/java/org/springframework/data/couchbase/domain/ReactiveUserColRepository.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2012-2020 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.data.couchbase.domain; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.data.couchbase.repository.DynamicProxyable; -import org.springframework.data.couchbase.repository.Query; -import org.springframework.data.couchbase.repository.ReactiveCouchbaseRepository; -import org.springframework.data.couchbase.repository.ScanConsistency; -import org.springframework.data.repository.query.Param; -import org.springframework.stereotype.Repository; - -import com.couchbase.client.java.json.JsonArray; -import com.couchbase.client.java.query.QueryScanConsistency; - -/** - * User Repository for tests - * - * @author Michael Nitschinger - * @author Michael Reiche - */ -@Repository -@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) -public interface ReactiveUserColRepository - extends ReactiveCouchbaseRepository, DynamicProxyable { - - Mono save(S var1); - - Flux findByFirstname(String firstname); - - Flux findByFirstnameIn(String... firstnames); - - Flux findByFirstnameIn(JsonArray firstnames); - - Flux findByFirstnameAndLastname(String firstname, String lastname); - - @Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and firstname = $1 and lastname = $2") - Flux getByFirstnameAndLastname(String firstname, String lastname); - - @Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and (firstname = $first or lastname = $last)") - Flux getByFirstnameOrLastname(@Param("first") String firstname, @Param("last") String lastname); - - Flux findByIdIsNotNullAndFirstnameEquals(String firstname); - - Flux findByVersionEqualsAndFirstnameEquals(Long version, String firstname); - -} diff --git a/src/test/java/org/springframework/data/couchbase/domain/UserColRepository.java b/src/test/java/org/springframework/data/couchbase/domain/UserColRepository.java deleted file mode 100644 index e1bbaa37..00000000 --- a/src/test/java/org/springframework/data/couchbase/domain/UserColRepository.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2012-2020 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.data.couchbase.domain; - -import java.util.List; - -import org.springframework.data.couchbase.repository.CouchbaseRepository; -import org.springframework.data.couchbase.repository.DynamicProxyable; -import org.springframework.data.couchbase.repository.Query; -import org.springframework.data.couchbase.repository.ScanConsistency; -import org.springframework.data.repository.query.Param; -import org.springframework.stereotype.Repository; - -import com.couchbase.client.java.json.JsonArray; -import com.couchbase.client.java.query.QueryScanConsistency; - -/** - * User Repository for tests - * - * @author Michael Nitschinger - * @author Michael Reiche - */ -@Repository -@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) -public interface UserColRepository extends CouchbaseRepository, DynamicProxyable { - - S save(S var1); - - List findByFirstname(String firstname); - - List findByFirstnameIn(String... firstnames); - - List findByFirstnameIn(JsonArray firstnames); - - List findByFirstnameAndLastname(String firstname, String lastname); - - @Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and firstname = $1 and lastname = $2") - List getByFirstnameAndLastname(String firstname, String lastname); - - @Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and (firstname = $first or lastname = $last)") - List getByFirstnameOrLastname(@Param("first") String firstname, @Param("last") String lastname); - - List findByIdIsNotNullAndFirstnameEquals(String firstname); - - List findByVersionEqualsAndFirstnameEquals(Long version, String firstname); - -} diff --git a/src/test/java/org/springframework/data/couchbase/domain/UserRepository.java b/src/test/java/org/springframework/data/couchbase/domain/UserRepository.java index 18b334d0..63293b33 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/UserRepository.java +++ b/src/test/java/org/springframework/data/couchbase/domain/UserRepository.java @@ -20,12 +20,10 @@ import java.util.List; import org.springframework.data.couchbase.repository.CouchbaseRepository; import org.springframework.data.couchbase.repository.Query; -import org.springframework.data.couchbase.repository.ScanConsistency; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import com.couchbase.client.java.json.JsonArray; -import com.couchbase.client.java.query.QueryScanConsistency; /** * User Repository for tests @@ -34,7 +32,6 @@ import com.couchbase.client.java.query.QueryScanConsistency; * @author Michael Reiche */ @Repository -@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) public interface UserRepository extends CouchbaseRepository { List findByFirstname(String firstname); @@ -54,5 +51,4 @@ public interface UserRepository extends CouchbaseRepository { List findByIdIsNotNullAndFirstnameEquals(String firstname); List findByVersionEqualsAndFirstnameEquals(Long version, String firstname); - } diff --git a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java index 5b5eda21..49ddbcee 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2019 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. @@ -21,12 +21,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.lang.reflect.Method; -import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -43,22 +41,21 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.dao.DataRetrievalFailureException; import org.springframework.data.auditing.DateTimeProvider; import org.springframework.data.couchbase.CouchbaseClientFactory; import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration; import org.springframework.data.couchbase.core.CouchbaseTemplate; -import org.springframework.data.couchbase.core.RemoveResult; import org.springframework.data.couchbase.core.query.N1QLExpression; +import org.springframework.data.couchbase.core.query.Query; import org.springframework.data.couchbase.core.query.QueryCriteria; import org.springframework.data.couchbase.domain.Address; import org.springframework.data.couchbase.domain.Airport; import org.springframework.data.couchbase.domain.AirportRepository; +import org.springframework.data.couchbase.domain.Iata; import org.springframework.data.couchbase.domain.NaiveAuditorAware; import org.springframework.data.couchbase.domain.Person; import org.springframework.data.couchbase.domain.PersonRepository; import org.springframework.data.couchbase.domain.User; -import org.springframework.data.couchbase.domain.UserAnnotated; import org.springframework.data.couchbase.domain.UserRepository; import org.springframework.data.couchbase.domain.time.AuditingDateTimeProvider; import org.springframework.data.couchbase.repository.auditing.EnableCouchbaseAuditing; @@ -76,14 +73,8 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; -import com.couchbase.client.core.error.AmbiguousTimeoutException; import com.couchbase.client.core.error.CouchbaseException; import com.couchbase.client.core.error.IndexExistsException; -import com.couchbase.client.core.error.IndexFailureException; -import com.couchbase.client.java.env.ClusterEnvironment; -import com.couchbase.client.java.json.JsonArray; -import com.couchbase.client.java.kv.MutationState; -import com.couchbase.client.java.query.QueryOptions; import com.couchbase.client.java.query.QueryScanConsistency; /** @@ -105,9 +96,6 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr @Autowired CouchbaseTemplate couchbaseTemplate; - String scopeName = "_default"; - String collectionName = "_default"; - @BeforeEach public void beforeEach() { try { @@ -194,17 +182,12 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr try { vie = new Airport("airports::vie", "vie", "low6"); vie = airportRepository.save(vie); - Airport airport2 = airportRepository - .withOptions(QueryOptions.queryOptions().scanConsistency(QueryScanConsistency.REQUEST_PLUS)) - .findByIata(vie.getIata()); - assertEquals(airport2, vie); - List airports = airportRepository.findAllByIata("vie"); assertEquals(1, airports.size()); Airport airport1 = airportRepository.findById(airports.get(0).getId()).get(); assertEquals(airport1.getIata(), vie.getIata()); - airport2 = airportRepository.findByIata(airports.get(0).getIata()); - assertEquals(airport2.getId(), vie.getId()); + Airport airport2 = airportRepository.findByIata(airports.get(0).getIata()); + assertEquals(airport1.getId(), vie.getId()); } finally { airportRepository.delete(vie); } @@ -218,9 +201,7 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr vie = airportRepository.save(vie); List airports = couchbaseTemplate.findByQuery(Airport.class) .withConsistency(QueryScanConsistency.REQUEST_PLUS) - .matching(org.springframework.data.couchbase.core.query.Query - .query(QueryCriteria.where(N1QLExpression.x("_class")).is("airport"))) - .all(); + .matching(new Query(QueryCriteria.where(N1QLExpression.x("_class")).is("airport"))).all(); assertFalse(airports.isEmpty(), "should have found aiport"); } finally { airportRepository.delete(vie); @@ -233,132 +214,18 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr try { vie = new Airport("airports::vie", "vie", "loww"); vie = airportRepository.save(vie); - Airport airport2 = airportRepository.findByIata(vie.getIata()); + Airport airport2 = airportRepository.findByIata(Iata.vie); assertNotNull(airport2, "should have found " + vie); assertEquals(airport2.getId(), vie.getId()); - } finally { airportRepository.delete(vie); } } - /** - * can test against _default._default without setting up additional scope/collection and also test for collections and - * scopes that do not exist These same tests should be repeated on non-default scope and collection in a test that - * supports collections - */ - @Test - @IgnoreWhen(missesCapabilities = { Capabilities.QUERY, Capabilities.COLLECTIONS }, clusterTypes = ClusterType.MOCKED) - void findBySimplePropertyWithCollection() { - - Airport vie = new Airport("airports::vie", "vie", "low7"); - try { - Airport saved = airportRepository.withScope(scopeName).withCollection(collectionName).save(vie); - // given collection (on scope used by template) - Airport airport2 = airportRepository.withCollection(collectionName) - .withOptions(QueryOptions.queryOptions().scanConsistency(QueryScanConsistency.REQUEST_PLUS)) - .iata(vie.getIata()); - assertEquals(saved, airport2); - - // given scope and collection - - Airport airport3 = airportRepository.withScope(scopeName).withCollection(collectionName) - .withOptions(QueryOptions.queryOptions().scanConsistency(QueryScanConsistency.REQUEST_PLUS)) - .iata(vie.getIata()); - assertEquals(saved, airport3); - - // given bad collection - assertThrows(IndexFailureException.class, - () -> airportRepository.withCollection("bogusCollection").iata(vie.getIata())); - - // given bad scope - assertThrows(IndexFailureException.class, () -> airportRepository.withScope("bogusScope").iata(vie.getIata())); - - } finally { - airportRepository.delete(vie); - } - } - - @Test - @IgnoreWhen(hasCapabilities = { Capabilities.COLLECTIONS }, clusterTypes = ClusterType.MOCKED) - void findBySimplePropertyWithCollectionFail() { - // can test against _default._default without setting up additional scope/collection - // the server will throw an exception if it doesn't support COLLECTIONS - Airport vie = new Airport("airports::vie", "vie", "low8"); - try { - - Airport saved = airportRepository.save(vie); - - assertThrows(CouchbaseException.class, () -> airportRepository.withScope("non_default_scope_name") - .withCollection(collectionName).iata(vie.getIata())); - - } finally { - airportRepository.delete(vie); - } - } - - @Test - void findBySimplePropertyWithOptions() { - - Airport vie = new Airport("airports::vie", "vie", "low9"); - JsonArray positionalParams = JsonArray.create().add("this parameter will be overridden"); - // JsonObject namedParams = JsonObject.create().put("$1", vie.getIata()); - try { - Airport saved = airportRepository.save(vie); - // Duration of 1 nano-second will cause timeout - assertThrows(AmbiguousTimeoutException.class, () -> airportRepository - .withOptions(QueryOptions.queryOptions().timeout(Duration.ofNanos(1))).iata(vie.getIata())); - - Airport airport3 = airportRepository.withOptions( - QueryOptions.queryOptions().scanConsistency(QueryScanConsistency.REQUEST_PLUS).parameters(positionalParams)) - .iata(vie.getIata()); - assertEquals(saved, airport3); - - } finally { - airportRepository.delete(vie); - } - - } - - @Test - public void saveNotBounded() { - // save() followed by query with NOT_BOUNDED will result in not finding the document - Airport vie = new Airport("airports::vie", "vie", "low9"); - Airport airport2 = null; - for (int i = 1; i <= 100; i++) { - // set version == 0 so save() will be an upsert, not a replace - Airport saved = airportRepository.save(vie.clearVersion()); - try { - airport2 = airportRepository - .withOptions(QueryOptions.queryOptions().scanConsistency(QueryScanConsistency.NOT_BOUNDED)) - .iata(saved.getIata()); - if (airport2 == null) { - break; - } - } catch (DataRetrievalFailureException drfe) { - airport2 = null; // - } finally { - // airportRepository.delete(vie); - // instead of delete, use removeResult to test QueryOptions.consistentWith() - RemoveResult removeResult = couchbaseTemplate.removeById().one(vie.getId()); - assertEquals(vie.getId(), removeResult.getId()); - assertTrue(removeResult.getCas() != 0); - assertTrue(removeResult.getMutationToken().isPresent()); - Airport airport3 = airportRepository - .withOptions(QueryOptions.queryOptions().scanConsistency(QueryScanConsistency.REQUEST_PLUS) - .consistentWith(MutationState.from(removeResult.getMutationToken().get()))) - .iata(vie.getIata()); - assertNull(airport3, "should have been removed"); - } - } - assertNull(airport2, "airport2 should have likely been null at least once"); - } - @Test public void testCas() { User user = new User("1", "Dave", "Wilson"); userRepository.save(user); - userRepository.findByFirstname("Dave"); user.setVersion(user.getVersion() - 1); assertThrows(DataIntegrityViolationException.class, () -> userRepository.save(user)); user.setVersion(0); @@ -366,20 +233,12 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr userRepository.delete(user); } - @Test - public void testExpiryAnnotation() { - UserAnnotated user = new UserAnnotated("1", "Dave", "Wilson"); - userRepository.save(user); - userRepository.findByFirstname("Dave"); - sleep(2000); - assertThrows(DataRetrievalFailureException.class, () -> userRepository.delete(user)); - } - @Test void count() { String[] iatas = { "JFK", "IAD", "SFO", "SJC", "SEA", "LAX", "PHX" }; try { + airportRepository.saveAll( Arrays.stream(iatas).map((iata) -> new Airport("airports::" + iata, iata, iata.toLowerCase(Locale.ROOT))) .collect(Collectors.toSet())); @@ -526,9 +385,12 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr Airport vienna = new Airport("airports::vie", "vie", "LOWW"); Airport frankfurt = new Airport("airports::fra", "fra", "EDDF"); Airport losAngeles = new Airport("airports::lax", "lax", "KLAX"); + try { airportRepository.saveAll(asList(vienna, frankfurt, losAngeles)); + airportRepository.deleteAllById(asList(vienna.getId(), losAngeles.getId())); + assertThat(airportRepository.findAll()).containsExactly(frankfurt); } finally { airportRepository.deleteAll(); @@ -568,9 +430,7 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr private void sleep(int millis) { try { Thread.sleep(millis); // so they are executed out-of-order - } catch (InterruptedException ie) { - ; - } + } catch (InterruptedException ie) {} } @Configuration @@ -603,16 +463,10 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr return new NaiveAuditorAware(); } - @Override - public void configureEnvironment(final ClusterEnvironment.Builder builder) { - builder.ioConfig().maxHttpConnections(11).idleHttpConnectionTimeout(Duration.ofSeconds(4)); - return; - } - @Bean(name = "dateTimeProviderRef") public DateTimeProvider testDateTimeProvider() { return new AuditingDateTimeProvider(); } - } + } diff --git a/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryKeyValueIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryKeyValueIntegrationTests.java index 81a6b077..77e6fe2a 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryKeyValueIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryKeyValueIntegrationTests.java @@ -73,8 +73,7 @@ public class ReactiveCouchbaseRepositoryKeyValueIntegrationTests extends Cluster Airport saved = airportRepository.save(vie).block(); Airport airport1 = airportRepository.findById(saved.getId()).block(); assertEquals(airport1, saved); - assertEquals(saved.getCreatedBy(), ReactiveNaiveAuditorAware.AUDITOR); // ReactiveNaiveAuditorAware will provide - // this + assertEquals(saved.getCreatedBy(), ReactiveNaiveAuditorAware.AUDITOR); // ReactiveNaiveAuditorAware will provide this } finally { airportRepository.delete(vie).block(); } diff --git a/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java index a5f015d5..ed07c651 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java @@ -130,17 +130,17 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends JavaIntegr Airport vie = new Airport("airports::vie", "vie", "low3"); Airport saved1 = airportRepository.save(vie).block(); Airport saved2 = airportRepository.save(vie.withId(UUID.randomUUID().toString())).block(); - try { - airportRepository.findAll().collectList().block(); // findAll has QueryScanConsistency; - Mono airport = airportRepository.findPolicySnapshotByPolicyIdAndEffectiveDateTime("any", 0); - System.out.println("------------------------------"); - System.out.println(airport.block()); - System.out.println("------------------------------"); - Flux airports = airportRepository.findPolicySnapshotAll(); - System.out.println(airports.collectList().block()); - System.out.println("------------------------------"); - Mono ap = getPolicyByIdAndEffectiveDateTime("x", Instant.now()); - System.out.println(ap.block()); + try { + airportRepository.findAll().collectList().block(); // findAll has QueryScanConsistency; + Mono airport = airportRepository.findPolicySnapshotByPolicyIdAndEffectiveDateTime("any", 0); + System.out.println("------------------------------"); + System.out.println(airport.block()); + System.out.println("------------------------------"); + Flux airports = airportRepository.findPolicySnapshotAll(); + System.out.println(airports.collectList().block()); + System.out.println("------------------------------"); + Mono ap = getPolicyByIdAndEffectiveDateTime("x", Instant.now()); + System.out.println(ap.block()); } finally { airportRepository.delete(saved1).block(); airportRepository.delete(saved2).block(); @@ -249,22 +249,6 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends JavaIntegr } } - @Test - void deleteOne() { - - Airport vienna = new Airport("airports::vie", "vie", "LOWW"); - - try { - Airport ap = airportRepository.save(vienna).block(); - assertEquals(vienna.getId(), ap.getId(), "should have saved what was provided"); - airportRepository.delete(vienna).as(StepVerifier::create).verifyComplete(); - - airportRepository.findAll().as(StepVerifier::create).verifyComplete(); - } finally { - airportRepository.deleteAll().block(); - } - } - @Configuration @EnableReactiveCouchbaseRepositories("org.springframework.data.couchbase") static class Config extends AbstractCouchbaseConfiguration { diff --git a/src/test/java/org/springframework/data/couchbase/repository/query/CouchbaseRepositoryQueryCollectionIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/query/CouchbaseRepositoryQueryCollectionIntegrationTests.java deleted file mode 100644 index f522671e..00000000 --- a/src/test/java/org/springframework/data/couchbase/repository/query/CouchbaseRepositoryQueryCollectionIntegrationTests.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2017-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.couchbase.repository.query; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.util.List; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.dao.DataRetrievalFailureException; -import org.springframework.data.couchbase.domain.Airport; -import org.springframework.data.couchbase.domain.AirportRepository; -import org.springframework.data.couchbase.domain.Config; -import org.springframework.data.couchbase.domain.User; -import org.springframework.data.couchbase.domain.UserCol; -import org.springframework.data.couchbase.domain.UserColRepository; -import org.springframework.data.couchbase.util.Capabilities; -import org.springframework.data.couchbase.util.ClusterType; -import org.springframework.data.couchbase.util.CollectionAwareIntegrationTests; -import org.springframework.data.couchbase.util.IgnoreWhen; - -import com.couchbase.client.core.error.IndexFailureException; -import com.couchbase.client.core.io.CollectionIdentifier; -import com.couchbase.client.java.json.JsonArray; -import com.couchbase.client.java.query.QueryOptions; -import com.couchbase.client.java.query.QueryScanConsistency; - -@IgnoreWhen(missesCapabilities = { Capabilities.QUERY, Capabilities.COLLECTIONS }, clusterTypes = ClusterType.MOCKED) -public class CouchbaseRepositoryQueryCollectionIntegrationTests extends CollectionAwareIntegrationTests { - - @Autowired AirportRepository airportRepository; - @Autowired UserColRepository userColRepository; - - @BeforeAll - public static void beforeAll() { - // first call the super method - callSuperBeforeAll(new Object() {}); - // then do processing for this class - } - - @AfterAll - public static void afterAll() { - // first do the processing for this class - // no-op - // then call the super method - callSuperAfterAll(new Object() {}); - } - - @BeforeEach - @Override - public void beforeEach() { - // first call the super method - super.beforeEach(); - // then do processing for this class - couchbaseTemplate.removeByQuery(User.class).inCollection(collectionName).all(); - couchbaseTemplate.removeByQuery(UserCol.class).inScope(otherScope).inCollection(otherCollection).all(); - ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class); - // seems that @Autowired is not adequate, so ... - airportRepository = (AirportRepository) ac.getBean("airportRepository"); - userColRepository = (UserColRepository) ac.getBean("userColRepository"); - } - - @AfterEach - @Override - public void afterEach() { - // first do processing for this class - // no-op - // then call the super method - super.afterEach(); - } - - @Test - public void myTest() { - - AirportRepository ar = airportRepository.withScope(scopeName).withCollection(collectionName); - Airport vie = new Airport("airports::vie", "vie", "loww"); - try { - Airport saved = ar.save(vie); - Airport airport2 = ar.save(saved); - } catch (Exception e) { - e.printStackTrace(); - throw e; - } finally { - ar.delete(vie); - } - - } - - /** - * can test against _default._default without setting up additional scope/collection and also test for collections and - * scopes that do not exist These same tests should be repeated on non-default scope and collection in a test that - * supports collections - */ - @Test - @IgnoreWhen(missesCapabilities = { Capabilities.QUERY, Capabilities.COLLECTIONS }, clusterTypes = ClusterType.MOCKED) - void findBySimplePropertyWithCollection() { - - Airport vie = new Airport("airports::vie", "vie", "loww"); - // create proxy with scope, collection - AirportRepository ar = airportRepository.withScope(scopeName).withCollection(collectionName); - try { - Airport saved = ar.save(vie); - - // valid scope, collection in options - Airport airport2 = ar.withCollection(collectionName) - .withOptions(QueryOptions.queryOptions().scanConsistency(QueryScanConsistency.REQUEST_PLUS)) - .iata(vie.getIata()); - assertEquals(saved, airport2); - - // given bad collectionName in fluent - assertThrows(IndexFailureException.class, () -> ar.withCollection("bogusCollection").iata(vie.getIata())); - - // given bad scopeName in fluent - assertThrows(IndexFailureException.class, () -> ar.withScope("bogusScope").iata(vie.getIata())); - - Airport airport6 = ar.withOptions(QueryOptions.queryOptions().scanConsistency(QueryScanConsistency.REQUEST_PLUS)) - .iata(vie.getIata()); - assertEquals(saved, airport6); - - } catch (Exception e) { - e.printStackTrace(); - throw e; - } finally { - ar.deleteAll(); - } - } - - @Test - void findBySimplePropertyWithOptions() { - - AirportRepository ar = airportRepository.withScope(scopeName).withCollection(collectionName); - Airport vie = new Airport("airports::vie", "vie", "loww"); - JsonArray positionalParams = JsonArray.create().add("\"this parameter will be overridden\""); - try { - Airport saved = ar.save(vie); - - Airport airport3 = ar.withOptions( - QueryOptions.queryOptions().scanConsistency(QueryScanConsistency.REQUEST_PLUS).parameters(positionalParams)) - .iata(vie.getIata()); - assertEquals(saved, airport3); - - } catch (Exception e) { - e.printStackTrace(); - throw e; - } finally { - ar.delete(vie); - } - } - - @Test - public void testScopeCollectionAnnotation() { - // template default scope is my_scope - // UserCol annotation scope is other_scope - UserCol user = new UserCol("1", "Dave", "Wilson"); - try { - UserCol saved = userColRepository.withCollection(otherCollection).save(user); // should use UserCol annotation - // scope - List found = userColRepository.withCollection(otherCollection).findByFirstname(user.getFirstname()); - assertEquals(saved, found.get(0), "should have found what was saved"); - List notfound = userColRepository.withScope(CollectionIdentifier.DEFAULT_SCOPE) - .withCollection(CollectionIdentifier.DEFAULT_COLLECTION).findByFirstname(user.getFirstname()); - assertEquals(0, notfound.size(), "should not have found what was saved"); - } finally { - try { - userColRepository.withScope(otherScope).withCollection(otherCollection).delete(user); - } catch (DataRetrievalFailureException drfe) {} - } - } - - // template default scope is my_scope - // UserCol annotation scope is other_scope - @Test - public void testScopeCollectionRepoWith() { - UserCol user = new UserCol("1", "Dave", "Wilson"); - try { - UserCol saved = userColRepository.withScope(scopeName).withCollection(collectionName).save(user); - List found = userColRepository.withScope(scopeName).withCollection(collectionName) - .findByFirstname(user.getFirstname()); - assertEquals(saved, found.get(0), "should have found what was saved"); - List notfound = userColRepository.withScope(CollectionIdentifier.DEFAULT_SCOPE) - .withCollection(CollectionIdentifier.DEFAULT_COLLECTION).findByFirstname(user.getFirstname()); - assertEquals(0, notfound.size(), "should not have found what was saved"); - userColRepository.withScope(scopeName).withCollection(collectionName).delete(user); - } finally { - try { - userColRepository.withScope(scopeName).withCollection(collectionName).delete(user); - } catch (DataRetrievalFailureException drfe) {} - } - } -} diff --git a/src/test/java/org/springframework/data/couchbase/repository/query/ReactiveCouchbaseRepositoryQueryCollectionIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/query/ReactiveCouchbaseRepositoryQueryCollectionIntegrationTests.java deleted file mode 100644 index 51f6a5ef..00000000 --- a/src/test/java/org/springframework/data/couchbase/repository/query/ReactiveCouchbaseRepositoryQueryCollectionIntegrationTests.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright 2017-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.couchbase.repository.query; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.util.List; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.dao.DataRetrievalFailureException; -import org.springframework.data.couchbase.domain.Airport; -import org.springframework.data.couchbase.domain.Config; -import org.springframework.data.couchbase.domain.ReactiveAirportRepository; -import org.springframework.data.couchbase.domain.ReactiveUserColRepository; -import org.springframework.data.couchbase.domain.User; -import org.springframework.data.couchbase.domain.UserCol; -import org.springframework.data.couchbase.util.Capabilities; -import org.springframework.data.couchbase.util.ClusterType; -import org.springframework.data.couchbase.util.CollectionAwareIntegrationTests; -import org.springframework.data.couchbase.util.IgnoreWhen; - -import com.couchbase.client.core.error.IndexFailureException; -import com.couchbase.client.core.io.CollectionIdentifier; -import com.couchbase.client.java.json.JsonArray; -import com.couchbase.client.java.query.QueryOptions; -import com.couchbase.client.java.query.QueryScanConsistency; - -@IgnoreWhen(missesCapabilities = { Capabilities.QUERY, Capabilities.COLLECTIONS }, clusterTypes = ClusterType.MOCKED) -public class ReactiveCouchbaseRepositoryQueryCollectionIntegrationTests extends CollectionAwareIntegrationTests { - - @Autowired ReactiveAirportRepository airportRepository; - @Autowired ReactiveUserColRepository userColRepository; - - @BeforeAll - public static void beforeAll() { - // first call the super method - callSuperBeforeAll(new Object() {}); - // then do processing for this class - } - - @AfterAll - public static void afterAll() { - // first do the processing for this class - // no-op - // then call the super method - callSuperAfterAll(new Object() {}); - } - - @BeforeEach - @Override - public void beforeEach() { - // first call the super method - super.beforeEach(); - // then do processing for this class - couchbaseTemplate.removeByQuery(User.class).inCollection(collectionName).all(); - couchbaseTemplate.removeByQuery(UserCol.class).inScope(otherScope).inCollection(otherCollection).all(); - - ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class); - // seems that @Autowired is not adequate, so ... - airportRepository = (ReactiveAirportRepository) ac.getBean("reactiveAirportRepository"); - userColRepository = (ReactiveUserColRepository) ac.getBean("reactiveUserColRepository"); - } - - @AfterEach - @Override - public void afterEach() { - // first do processing for this class - // no-op - // then call the super method - super.afterEach(); - } - - @Test - public void myTest() { - - ReactiveAirportRepository ar = airportRepository.withScope(scopeName).withCollection(collectionName); - Airport vie = new Airport("airports::vie", "vie", "loww"); - try { - Airport saved = ar.save(vie).block(); - Airport airport2 = ar.save(saved).block(); - } catch (Exception e) { - e.printStackTrace(); - throw e; - } finally { - ar.delete(vie).block(); - } - - } - - /** - * can test against _default._default without setting up additional scope/collection and also test for collections and - * scopes that do not exist These same tests should be repeated on non-default scope and collection in a test that - * supports collections - */ - @Test - @IgnoreWhen(missesCapabilities = { Capabilities.QUERY, Capabilities.COLLECTIONS }, clusterTypes = ClusterType.MOCKED) - void findBySimplePropertyWithCollection() { - - Airport vie = new Airport("airports::vie", "vie", "loww"); - // create proxy with scope, collection - ReactiveAirportRepository ar = airportRepository.withScope(scopeName).withCollection(collectionName); - try { - Airport saved = ar.save(vie).block(); - - // valid scope, collection in options - Airport airport2 = ar.withOptions(QueryOptions.queryOptions().scanConsistency(QueryScanConsistency.REQUEST_PLUS)) - .iata(vie.getIata()).block(); - assertEquals(saved, airport2); - - // given bad collectionName in fluent - assertThrows(IndexFailureException.class, () -> ar.withCollection("bogusCollection").iata(vie.getIata()).block()); - - // given bad scopeName in fluent - assertThrows(IndexFailureException.class, () -> ar.withScope("bogusScope").iata(vie.getIata()).block()); - - Airport airport6 = ar.withOptions(QueryOptions.queryOptions().scanConsistency(QueryScanConsistency.REQUEST_PLUS)) - .iata(vie.getIata()).block(); - assertEquals(saved, airport6); - - } catch (Exception e) { - e.printStackTrace(); - throw e; - } finally { - ar.deleteAll().block(); - } - } - - @Test - void findBySimplePropertyWithOptions() { - - Airport vie = new Airport("airports::vie", "vie", "loww"); - ReactiveAirportRepository ar = airportRepository.withScope(scopeName).withCollection(collectionName); - JsonArray positionalParams = JsonArray.create().add("\"this parameter will be overridden\""); - try { - Airport saved = ar.save(vie).block(); - - Airport airport3 = ar.withOptions( - QueryOptions.queryOptions().scanConsistency(QueryScanConsistency.REQUEST_PLUS).parameters(positionalParams)) - .iata(vie.getIata()).block(); - assertEquals(saved, airport3); - - } catch (Exception e) { - e.printStackTrace(); - throw e; - } finally { - ar.delete(vie).block(); - } - } - - @Test - public void testScopeCollectionAnnotation() { - // template default scope is my_scope - // UserCol annotation scope is other_scope - UserCol user = new UserCol("1", "Dave", "Wilson"); - try { - UserCol saved = userColRepository.withCollection(otherCollection).save(user).block(); // should use UserCol - // annotation - // scope - List found = userColRepository.withCollection(otherCollection).findByFirstname(user.getFirstname()) - .collectList().block(); - assertEquals(saved, found.get(0), "should have found what was saved"); - List notfound = userColRepository.withScope(CollectionIdentifier.DEFAULT_SCOPE) - .withCollection(CollectionIdentifier.DEFAULT_COLLECTION).findByFirstname(user.getFirstname()).collectList() - .block(); - assertEquals(0, notfound.size(), "should not have found what was saved"); - } finally { - try { - userColRepository.withScope(otherScope).withCollection(otherCollection).delete(user); - } catch (DataRetrievalFailureException drfe) {} - } - } - - // template default scope is my_scope - // UserCol annotation scope is other_scope - @Test - public void testScopeCollectionRepoWith() { - UserCol user = new UserCol("1", "Dave", "Wilson"); - try { - UserCol saved = userColRepository.withScope(scopeName).withCollection(collectionName).save(user).block(); - List found = userColRepository.withScope(scopeName).withCollection(collectionName) - .findByFirstname(user.getFirstname()).collectList().block(); - assertEquals(saved, found.get(0), "should have found what was saved"); - List notfound = userColRepository.withScope(CollectionIdentifier.DEFAULT_SCOPE) - .withCollection(CollectionIdentifier.DEFAULT_COLLECTION).findByFirstname(user.getFirstname()).collectList() - .block(); - assertEquals(0, notfound.size(), "should not have found what was saved"); - userColRepository.withScope(scopeName).withCollection(collectionName).delete(user).block(); - } finally { - try { - userColRepository.withScope(scopeName).withCollection(collectionName).delete(user).block(); - } catch (DataRetrievalFailureException drfe) {} - } - } -} diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml index 9d05fdc1..57bffc18 100644 --- a/src/test/resources/logback.xml +++ b/src/test/resources/logback.xml @@ -22,7 +22,7 @@ - log additional debug info during automatic index creation --> - " + "