diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableInsertByIdOperation.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableInsertByIdOperation.java index 6ab0ea948..761f2e74c 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableInsertByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableInsertByIdOperation.java @@ -26,20 +26,22 @@ public interface ExecutableInsertByIdOperation { ExecutableInsertById insertById(Class domainType); - interface TerminatingInsertById { + interface TerminatingInsertById extends OneAndAll{ + @Override T one(T object); + @Override Collection all(Collection objects); } - interface InsertByIdWithCollection extends TerminatingInsertById { + interface InsertByIdWithCollection extends TerminatingInsertById, InCollection { TerminatingInsertById inCollection(String collection); } - interface InsertByIdWithDurability extends InsertByIdWithCollection { + interface InsertByIdWithDurability extends InsertByIdWithCollection, WithDurability { InsertByIdWithCollection withDurability(DurabilityLevel durabilityLevel); @@ -47,8 +49,9 @@ interface InsertByIdWithDurability extends InsertByIdWithCollection { } - interface InsertByIdWithExpiry extends InsertByIdWithDurability { + interface InsertByIdWithExpiry extends InsertByIdWithDurability, WithExpiry { + @Override InsertByIdWithDurability withExpiry(Duration 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 7287f6966..94a6cf682 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByIdOperation.java @@ -34,12 +34,12 @@ interface TerminatingRemoveById { } - interface RemoveByIdWithCollection extends TerminatingRemoveById { + interface RemoveByIdWithCollection extends TerminatingRemoveById, InCollection { TerminatingRemoveById inCollection(String collection); } - interface RemoveByIdWithDurability extends RemoveByIdWithCollection { + interface RemoveByIdWithDurability extends RemoveByIdWithCollection, WithDurability { RemoveByIdWithCollection withDurability(DurabilityLevel durabilityLevel); diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableReplaceByIdOperation.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableReplaceByIdOperation.java index 98fc758da..b89c7ae89 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableReplaceByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableReplaceByIdOperation.java @@ -19,6 +19,7 @@ import java.util.Collection; import com.couchbase.client.core.msg.kv.DurabilityLevel; +import com.couchbase.client.java.kv.IncrementOptions; import com.couchbase.client.java.kv.PersistTo; import com.couchbase.client.java.kv.ReplicateTo; @@ -26,20 +27,22 @@ public interface ExecutableReplaceByIdOperation { ExecutableReplaceById replaceById(Class domainType); - interface TerminatingReplaceById { + interface TerminatingReplaceById extends OneAndAll { + @Override T one(T object); + @Override Collection all(Collection objects); } - interface ReplaceByIdWithCollection extends TerminatingReplaceById { + interface ReplaceByIdWithCollection extends TerminatingReplaceById , InCollection { TerminatingReplaceById inCollection(String collection); } - interface ReplaceByIdWithDurability extends ReplaceByIdWithCollection { + interface ReplaceByIdWithDurability extends ReplaceByIdWithCollection, WithDurability { ReplaceByIdWithCollection withDurability(DurabilityLevel durabilityLevel); @@ -47,8 +50,9 @@ interface ReplaceByIdWithDurability extends ReplaceByIdWithCollection { } - interface ReplaceByIdWithExpiry extends ReplaceByIdWithDurability { + interface ReplaceByIdWithExpiry extends ReplaceByIdWithDurability, WithExpiry { + @Override ReplaceByIdWithDurability withExpiry(final Duration expiry); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableUpsertByIdOperation.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableUpsertByIdOperation.java index 3d919a8c2..a21ba69d7 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableUpsertByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableUpsertByIdOperation.java @@ -26,20 +26,22 @@ public interface ExecutableUpsertByIdOperation { ExecutableUpsertById upsertById(Class domainType); - interface TerminatingUpsertById { + interface TerminatingUpsertById extends OneAndAll{ + @Override T one(T object); + @Override Collection all(Collection objects); } - interface UpsertByIdWithCollection extends TerminatingUpsertById { + interface UpsertByIdWithCollection extends TerminatingUpsertById, InCollection { TerminatingUpsertById inCollection(String collection); } - interface UpsertByIdWithDurability extends UpsertByIdWithCollection { + interface UpsertByIdWithDurability extends UpsertByIdWithCollection, WithDurability { UpsertByIdWithCollection withDurability(DurabilityLevel durabilityLevel); @@ -47,8 +49,9 @@ interface UpsertByIdWithDurability extends UpsertByIdWithCollection { } - interface UpsertByIdWithExpiry extends UpsertByIdWithDurability { + interface UpsertByIdWithExpiry extends UpsertByIdWithDurability, WithExpiry { + @Override UpsertByIdWithDurability withExpiry(Duration expiry); } diff --git a/src/main/java/org/springframework/data/couchbase/core/InCollection.java b/src/main/java/org/springframework/data/couchbase/core/InCollection.java new file mode 100644 index 000000000..d9fb292ef --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/InCollection.java @@ -0,0 +1,27 @@ +/* + * Copyright 2020 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core; + +/** + * A common interface for all of Insert, Replace, Upsert that take collection + * + * @author Michael Reiche + * + * @param - the entity class + */ +public interface InCollection { + Object inCollection(String collection); +} diff --git a/src/main/java/org/springframework/data/couchbase/core/OneAndAll.java b/src/main/java/org/springframework/data/couchbase/core/OneAndAll.java new file mode 100644 index 000000000..fd0077ff3 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/OneAndAll.java @@ -0,0 +1,32 @@ +/* + * Copyright 2020 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core; + +import java.util.Collection; + +/** + * A common interface for all of Insert, Replace, Upsert + * + * @author Michael Reiche + * + * @param - the entity class + */ +public interface OneAndAll { + + T one(T object); + + Collection all(Collection objects); +} diff --git a/src/main/java/org/springframework/data/couchbase/core/OneAndAllReactive.java b/src/main/java/org/springframework/data/couchbase/core/OneAndAllReactive.java new file mode 100644 index 000000000..251964ce1 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/OneAndAllReactive.java @@ -0,0 +1,35 @@ +/* + * Copyright 2020 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.Collection; + +/** + * A common interface for all of Insert, Replace, Upsert + * + * @author Michael Reiche + * + * @param - the entity class + */ + +public interface OneAndAllReactive { + Mono one(T object); + + Flux all(Collection objects); +} 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 54e10163c..4bc9ffa2f 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperation.java @@ -29,7 +29,7 @@ public interface ReactiveInsertByIdOperation { ReactiveInsertById insertById(Class domainType); - interface TerminatingInsertById { + interface TerminatingInsertById extends OneAndAllReactive{ Mono one(T object); @@ -37,12 +37,12 @@ interface TerminatingInsertById { } - interface InsertByIdWithCollection extends TerminatingInsertById { + interface InsertByIdWithCollection extends TerminatingInsertById, InCollection { TerminatingInsertById inCollection(String collection); } - interface InsertByIdWithDurability extends InsertByIdWithCollection { + interface InsertByIdWithDurability extends InsertByIdWithCollection, WithDurability { InsertByIdWithCollection withDurability(DurabilityLevel durabilityLevel); @@ -50,7 +50,7 @@ interface InsertByIdWithDurability extends InsertByIdWithCollection { } - interface InsertByIdWithExpiry extends InsertByIdWithDurability { + interface InsertByIdWithExpiry extends InsertByIdWithDurability, WithExpiry{ InsertByIdWithDurability withExpiry(Duration expiry); } 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 181bbf26e..01a9faa30 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperationSupport.java @@ -15,7 +15,6 @@ */ package org.springframework.data.couchbase.core; -import org.springframework.data.couchbase.core.mapping.Document; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -72,7 +71,7 @@ public Mono one(T object) { return Mono.just(object).flatMap(o -> { CouchbaseDocument converted = template.support().encodeEntity(o); return template.getCollection(collection).reactive() - .insert(converted.getId(), converted.export(), buildInsertOptions()).map(result -> { + .insert(converted.getId(), converted.export(), buildInsertOptions(converted)).map(result -> { Object updatedObject = template.support().applyUpdatedId(o, converted.getId()); return (T) template.support().applyUpdatedCas(updatedObject, result.cas()); }); @@ -90,7 +89,7 @@ public Flux all(Collection objects) { return Flux.fromIterable(objects).flatMap(this::one); } - private InsertOptions buildInsertOptions() { + private InsertOptions buildInsertOptions(CouchbaseDocument doc) { // CouchbaseDocument converted final InsertOptions options = InsertOptions.insertOptions(); if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) { options.durability(persistTo, replicateTo); @@ -99,10 +98,8 @@ private InsertOptions buildInsertOptions() { } if (expiry != null && !expiry.isZero()) { options.expiry(expiry); - } else if (domainType.isAnnotationPresent(Document.class)) { - Document documentAnn = domainType.getAnnotation(Document.class); - long durationSeconds = documentAnn.expiryUnit().toSeconds(documentAnn.expiry()); - options.expiry(Duration.ofSeconds(durationSeconds)); + } else if (doc.getExpiration() != 0) { + options.expiry(Duration.ofSeconds(doc.getExpiration())); } return options; } 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 d26519af9..d1cc5fa65 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByIdOperation.java @@ -36,12 +36,12 @@ interface TerminatingRemoveById { } - interface RemoveByIdWithCollection extends TerminatingRemoveById { + interface RemoveByIdWithCollection extends TerminatingRemoveById, InCollection { TerminatingRemoveById inCollection(String collection); } - interface RemoveByIdWithDurability extends RemoveByIdWithCollection { + interface RemoveByIdWithDurability extends RemoveByIdWithCollection, WithDurability { RemoveByIdWithCollection withDurability(DurabilityLevel durabilityLevel); diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperation.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperation.java index acdf825f1..89bf0ca52 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperation.java @@ -29,7 +29,7 @@ public interface ReactiveReplaceByIdOperation { ReactiveReplaceById replaceById(Class domainType); - interface TerminatingReplaceById { + interface TerminatingReplaceById extends OneAndAllReactive { Mono one(T object); @@ -37,12 +37,12 @@ interface TerminatingReplaceById { } - interface ReplaceByIdWithCollection extends TerminatingReplaceById { + interface ReplaceByIdWithCollection extends TerminatingReplaceById, InCollection { TerminatingReplaceById inCollection(String collection); } - interface ReplaceByIdWithDurability extends ReplaceByIdWithCollection { + interface ReplaceByIdWithDurability extends ReplaceByIdWithCollection, WithDurability { ReplaceByIdWithCollection withDurability(DurabilityLevel durabilityLevel); @@ -50,7 +50,7 @@ interface ReplaceByIdWithDurability extends ReplaceByIdWithCollection { } - interface ReplaceByIdWithExpiry extends ReplaceByIdWithDurability { + interface ReplaceByIdWithExpiry extends ReplaceByIdWithDurability, WithExpiry { ReplaceByIdWithDurability withExpiry(final Duration expiry); } 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 787bdb7dd..3c5936227 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperationSupport.java @@ -22,7 +22,6 @@ import java.util.Collection; import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; -import org.springframework.data.couchbase.core.mapping.Document; import org.springframework.util.Assert; import com.couchbase.client.core.msg.kv.DurabilityLevel; @@ -72,8 +71,8 @@ public Mono one(T object) { return Mono.just(object).flatMap(o -> { CouchbaseDocument converted = template.support().encodeEntity(o); return template.getCollection(collection).reactive() - .replace(converted.getId(), converted.export(), buildReplaceOptions(o)).map(result -> - (T)template.support().applyUpdatedCas(o, result.cas())); + .replace(converted.getId(), converted.export(), buildReplaceOptions(o, converted)) + .map(result -> (T) template.support().applyUpdatedCas(o, result.cas())); }).onErrorMap(throwable -> { if (throwable instanceof RuntimeException) { return template.potentiallyConvertRuntimeException((RuntimeException) throwable); @@ -88,7 +87,7 @@ public Flux all(Collection objects) { return Flux.fromIterable(objects).flatMap(this::one); } - private ReplaceOptions buildReplaceOptions(T object) { + private ReplaceOptions buildReplaceOptions(T object, CouchbaseDocument doc) { final ReplaceOptions options = ReplaceOptions.replaceOptions(); if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) { options.durability(persistTo, replicateTo); @@ -97,10 +96,8 @@ private ReplaceOptions buildReplaceOptions(T object) { } if (expiry != null && !expiry.isZero()) { options.expiry(expiry); - } else if (domainType.isAnnotationPresent(Document.class)) { - Document documentAnn = domainType.getAnnotation(Document.class); - long durationSeconds = documentAnn.expiryUnit().toSeconds(documentAnn.expiry()); - options.expiry(Duration.ofSeconds(durationSeconds)); + } else if (doc.getExpiration() != 0) { + options.expiry(Duration.ofSeconds(doc.getExpiration())); } long cas = template.support().getCas(object); options.cas(cas); diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperation.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperation.java index da0df85bd..b2c3c700d 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperation.java @@ -29,7 +29,7 @@ public interface ReactiveUpsertByIdOperation { ReactiveUpsertById upsertById(Class domainType); - interface TerminatingUpsertById { + interface TerminatingUpsertById extends OneAndAllReactive{ Mono one(T object); @@ -37,12 +37,12 @@ interface TerminatingUpsertById { } - interface UpsertByIdWithCollection extends TerminatingUpsertById { + interface UpsertByIdWithCollection extends TerminatingUpsertById, InCollection { TerminatingUpsertById inCollection(String collection); } - interface UpsertByIdWithDurability extends UpsertByIdWithCollection { + interface UpsertByIdWithDurability extends UpsertByIdWithCollection, WithDurability { UpsertByIdWithCollection withDurability(DurabilityLevel durabilityLevel); @@ -50,7 +50,7 @@ interface UpsertByIdWithDurability extends UpsertByIdWithCollection { } - interface UpsertByIdWithExpiry extends UpsertByIdWithDurability { + interface UpsertByIdWithExpiry extends UpsertByIdWithDurability, WithExpiry { UpsertByIdWithDurability withExpiry(Duration expiry); } 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 c4e8fb690..0e18cf2e8 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperationSupport.java @@ -15,9 +15,6 @@ */ package org.springframework.data.couchbase.core; -import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity; -import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty; -import org.springframework.data.couchbase.core.mapping.Document; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -74,7 +71,7 @@ public Mono one(T object) { return Mono.just(object).flatMap(o -> { CouchbaseDocument converted = template.support().encodeEntity(o); return template.getCollection(collection).reactive() - .upsert(converted.getId(), converted.export(), buildUpsertOptions()).map(result -> { + .upsert(converted.getId(), converted.export(), buildUpsertOptions(converted)).map(result -> { Object updatedObject = template.support().applyUpdatedId(o, converted.getId()); return (T) template.support().applyUpdatedCas(updatedObject, result.cas()); }); @@ -92,7 +89,7 @@ public Flux all(Collection objects) { return Flux.fromIterable(objects).flatMap(this::one); } - private UpsertOptions buildUpsertOptions() { + private UpsertOptions buildUpsertOptions(CouchbaseDocument doc) { final UpsertOptions options = UpsertOptions.upsertOptions(); if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) { options.durability(persistTo, replicateTo); @@ -101,10 +98,8 @@ private UpsertOptions buildUpsertOptions() { } if (expiry != null && !expiry.isZero()) { options.expiry(expiry); - } else if (domainType.isAnnotationPresent(Document.class)) { - Document documentAnn = domainType.getAnnotation(Document.class); - long durationSeconds = documentAnn.expiryUnit().toSeconds(documentAnn.expiry()); - options.expiry(Duration.ofSeconds(durationSeconds)); + } else if (doc.getExpiration() != 0) { + options.expiry(Duration.ofSeconds(doc.getExpiration())); } return options; } diff --git a/src/main/java/org/springframework/data/couchbase/core/WithDurability.java b/src/main/java/org/springframework/data/couchbase/core/WithDurability.java new file mode 100644 index 000000000..d829120d7 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/WithDurability.java @@ -0,0 +1,33 @@ +/* + * Copyright 2020 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core; + +import com.couchbase.client.core.msg.kv.DurabilityLevel; +import com.couchbase.client.java.kv.PersistTo; +import com.couchbase.client.java.kv.ReplicateTo; + +/** + * A common interface for all of Insert, Replace, Upsert that take Durability + * + * @author Michael Reiche + * + * @param - the entity class + */ +public interface WithDurability { + Object withDurability(DurabilityLevel durabilityLevel); + + Object withDurability(PersistTo persistTo, ReplicateTo replicateTo); +} diff --git a/src/main/java/org/springframework/data/couchbase/core/WithExpiry.java b/src/main/java/org/springframework/data/couchbase/core/WithExpiry.java new file mode 100644 index 000000000..7757dd978 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/WithExpiry.java @@ -0,0 +1,29 @@ +/* + * Copyright 2020 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core; + +import java.time.Duration; + +/** + * A common interface for all of Insert, Replace, Upsert that take expiry + * + * @author Michael Reiche + * + * @param - the entity class + */ +public interface WithExpiry { + Object withExpiry(Duration expiry); +} 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 f95f4ee42..7d245a316 100644 --- a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java @@ -27,9 +27,14 @@ import static org.springframework.data.couchbase.config.BeanNames.REACTIVE_COUCHBASE_TEMPLATE; import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.time.Duration; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; +import com.couchbase.client.java.manager.query.CreatePrimaryQueryIndexOptions;; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -37,14 +42,17 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.dao.DataRetrievalFailureException; import org.springframework.dao.DuplicateKeyException; import org.springframework.data.couchbase.CouchbaseClientFactory; import org.springframework.data.couchbase.SimpleCouchbaseClientFactory; +import org.springframework.data.couchbase.core.ExecutableReplaceByIdOperation.ExecutableReplaceById; +import org.springframework.data.couchbase.core.ExecutableRemoveByIdOperation.ExecutableRemoveById; + import org.springframework.data.couchbase.domain.Config; import org.springframework.data.couchbase.domain.PersonValue; import org.springframework.data.couchbase.domain.User; import org.springframework.data.couchbase.domain.UserAnnotated; +import org.springframework.data.couchbase.domain.UserAnnotated2; import org.springframework.data.couchbase.util.ClusterAwareIntegrationTests; import org.springframework.data.couchbase.util.ClusterType; import org.springframework.data.couchbase.util.IgnoreWhen; @@ -69,6 +77,8 @@ class CouchbaseTemplateKeyValueIntegrationTests extends ClusterAwareIntegrationT static void beforeAll() { couchbaseClientFactory = new SimpleCouchbaseClientFactory(connectionString(), authenticator(), bucketName()); couchbaseClientFactory.getBucket().waitUntilReady(Duration.ofSeconds(10)); + couchbaseClientFactory.getCluster().queryIndexes().createPrimaryIndex(bucketName(), + CreatePrimaryQueryIndexOptions.createPrimaryQueryIndexOptions().ignoreIfExists(true)); } @AfterAll @@ -81,6 +91,9 @@ void beforeEach() { ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class); couchbaseTemplate = (CouchbaseTemplate) ac.getBean(COUCHBASE_TEMPLATE); reactiveCouchbaseTemplate = (ReactiveCouchbaseTemplate) ac.getBean(REACTIVE_COUCHBASE_TEMPLATE); + couchbaseTemplate.removeByQuery(User.class).all(); + couchbaseTemplate.removeByQuery(UserAnnotated.class).all(); + couchbaseTemplate.removeByQuery(UserAnnotated2.class).all(); } @Test @@ -103,88 +116,78 @@ void upsertAndFindById() { } @Test - void upsertWithDurability() { - User user = new User(UUID.randomUUID().toString(), "firstname", "lastname"); - User modified = couchbaseTemplate.upsertById(User.class).withDurability(PersistTo.ACTIVE, ReplicateTo.NONE) - .one(user); - assertEquals(user, modified); - User found = couchbaseTemplate.findById(User.class).one(user.getId()); - assertEquals(user, found); - couchbaseTemplate.removeById().one(user.getId()); - } - - @Test - void upsertWithExpiry() { - User user = new User(UUID.randomUUID().toString(), "firstname", "lastname"); - try { - User modified = couchbaseTemplate.upsertById(User.class).withExpiry(Duration.ofSeconds(1)).one(user); - assertEquals(user, modified); - sleepSecs(2); - User found = couchbaseTemplate.findById(User.class).one(user.getId()); - assertNull(found, "found should have been null as document should be expired"); - } finally { - try { - couchbaseTemplate.removeById().one(user.getId()); - } catch (DataRetrievalFailureException e) { - // + void withDurability() + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { + Class clazz = User.class; // for now, just User.class. There is no Durability annotation. + // insert, replace, upsert + for (OneAndAll operator : new OneAndAll[] { couchbaseTemplate.insertById(clazz), + couchbaseTemplate.replaceById(clazz), couchbaseTemplate.upsertById(clazz)}) { + // create an entity of type clazz + Constructor cons = clazz.getConstructor(String.class, String.class, String.class); + User user = (User) cons.newInstance("" + operator.getClass().getSimpleName() + "_" + clazz.getSimpleName(), + "firstname", "lastname"); + + if (clazz.equals(User.class)) { // User.java doesn't have an durability annotation + operator = (OneAndAll) ((WithDurability) operator).withDurability(PersistTo.ACTIVE, ReplicateTo.NONE); } - } - } - @Test - void upsertWithExpiryAnnotation() { - UserAnnotated user = new UserAnnotated(UUID.randomUUID().toString(), "firstname", "lastname"); - try { - UserAnnotated modified = couchbaseTemplate.upsertById(UserAnnotated.class).one(user); - assertEquals(user, modified); - sleepSecs(6); + // if replace, we need to insert a document to replace + if (operator instanceof ExecutableReplaceById) { + couchbaseTemplate.insertById(User.class).one(user); + } + // call to insert/replace/update + User returned = (User)operator.one(user); + assertEquals(user, returned); User found = couchbaseTemplate.findById(User.class).one(user.getId()); - assertNull(found, "found should have been null as document should be expired"); - } finally { - try { - couchbaseTemplate.removeById().one(user.getId()); - } catch (DataRetrievalFailureException e) { - // + assertEquals(user, found); + + if (operator instanceof ExecutableReplaceById) { + couchbaseTemplate.removeById().withDurability(PersistTo.ACTIVE, ReplicateTo.NONE).one(user.getId()); + User removed = (User) couchbaseTemplate.findById(user.getClass()).one(user.getId()); + assertNull(removed, "found should have been null as document should be removed"); } } + } @Test - void replaceWithExpiry() { - User user = new User(UUID.randomUUID().toString(), "firstname", "lastname"); - try { - User modified = couchbaseTemplate.upsertById(User.class).withExpiry(Duration.ofSeconds(1)).one(user); - couchbaseTemplate.replaceById(User.class).withExpiry(Duration.ofSeconds(1)).one(user); - assertEquals(user, modified); - sleepSecs(2); - User found = couchbaseTemplate.findById(User.class).one(user.getId()); - assertNull(found, "found should have been null as document should be expired"); - } finally { - try { - couchbaseTemplate.removeById().one(user.getId()); - } catch (DataRetrievalFailureException e) { - // + void withExpiryAndExpiryAnnotation() + throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { + // ( withExpiry(), expiry=1, expiryExpression=${myExpiry} ) X ( insert, + // replace, upsert ) + Set users = new HashSet<>(); // set of all documents we will insert + // Entity classes + for (Class clazz : new Class[] { User.class, UserAnnotated.class, UserAnnotated2.class }) { + // insert, replace, upsert + for (OneAndAll operator : new OneAndAll[] { couchbaseTemplate.insertById(clazz), + couchbaseTemplate.replaceById(clazz), couchbaseTemplate.upsertById(clazz) }) { + + // create an entity of type clazz + Constructor cons = clazz.getConstructor(String.class, String.class, String.class); + User user = (User) cons.newInstance("" + operator.getClass().getSimpleName() + "_" + clazz.getSimpleName(), + "firstname", "lastname"); + + if (clazz.equals(User.class)) { // User.java doesn't have an expiry annotation + operator = (OneAndAll) ((WithExpiry) operator).withExpiry(Duration.ofSeconds(1)); + } + + // if replace or remove, we need to insert a document to replace + if (operator instanceof ExecutableReplaceById || operator instanceof ExecutableRemoveById) { + couchbaseTemplate.insertById(User.class).one(user); + } + // call to insert/replace/update + User returned = (User)operator.one(user); + assertEquals(user, returned); + users.add(user); } } - } - - @Test - void replaceWithExpiryAnnotation() { - UserAnnotated user = new UserAnnotated(UUID.randomUUID().toString(), "firstname", "lastname"); - try { - UserAnnotated modified = couchbaseTemplate.upsertById(UserAnnotated.class).one(user); - modified = couchbaseTemplate.replaceById(UserAnnotated.class).one(user); - assertEquals(user, modified); - sleepSecs(6); - User found = couchbaseTemplate.findById(UserAnnotated.class).one(user.getId()); + // check that they are gone after a few seconds. + sleepSecs(4); + for (User user : users) { + User found = (User) couchbaseTemplate.findById(user.getClass()).one(user.getId()); assertNull(found, "found should have been null as document should be expired"); - } finally { - try { - couchbaseTemplate.removeById().one(user.getId()); - } catch (DataRetrievalFailureException e) { - // - } } + } @Test @@ -227,10 +230,7 @@ void insertById() { User user = new User(UUID.randomUUID().toString(), "firstname", "lastname"); User inserted = couchbaseTemplate.insertById(User.class).one(user); assertEquals(user, inserted); - assertThrows(DuplicateKeyException.class, () -> couchbaseTemplate.insertById(User.class).one(user)); - couchbaseTemplate.removeById().one(user.getId()); - } @Test @@ -239,47 +239,7 @@ void insertByIdwithDurability() { User inserted = couchbaseTemplate.insertById(User.class).withDurability(PersistTo.ACTIVE, ReplicateTo.NONE) .one(user); assertEquals(user, inserted); - assertThrows(DuplicateKeyException.class, () -> couchbaseTemplate.insertById(User.class).one(user)); - couchbaseTemplate.removeById().one(user.getId()); - - } - - @Test - void insertByIdwithExpiry() { - User user = new User(UUID.randomUUID().toString(), "firstname", "lastname"); - try { - User inserted = couchbaseTemplate.insertById(User.class).withExpiry(Duration.ofSeconds(1)).one(user); - assertEquals(user, inserted); - sleepSecs(2); - User found = couchbaseTemplate.findById(User.class).one(user.getId()); - assertNull(found, "found should have been null as document should be expired"); - } finally { - try { - couchbaseTemplate.removeById().one(user.getId()); - } catch (DataRetrievalFailureException e) { - // ignore - } - } - - } - - @Test - void insertWithExpiryAnnotation() { - UserAnnotated user = new UserAnnotated(UUID.randomUUID().toString(), "firstname", "lastname"); - try { - UserAnnotated inserted = couchbaseTemplate.insertById(UserAnnotated.class).one(user); - assertEquals(user, inserted); - sleepSecs(6); - User found = couchbaseTemplate.findById(User.class).one(user.getId()); - assertNull(found, "found should have been null as document should be expired"); - } finally { - try { - couchbaseTemplate.removeById().one(user.getId()); - } catch (DataRetrievalFailureException e) { - // ignore - } - } } @Test @@ -292,52 +252,44 @@ void existsById() { assertEquals(user, inserted); assertTrue(couchbaseTemplate.existsById().one(id)); - couchbaseTemplate.removeById().one(user.getId()); } @Test @IgnoreWhen(clusterTypes = ClusterType.MOCKED) void saveAndFindImmutableById() { - PersonValue personValue = new PersonValue(null, 123, "f", "l"); - System.out.println("personValue: " + personValue); - // personValue = personValue.withVersion(123); + PersonValue personValue = new PersonValue(UUID.randomUUID().toString(), 123, "f", "l"); PersonValue inserted = null; PersonValue upserted = null; PersonValue replaced = null; - try { + inserted = couchbaseTemplate.insertById(PersonValue.class).one(personValue); + assertNotEquals(0, inserted.getVersion()); + PersonValue foundInserted = couchbaseTemplate.findById(PersonValue.class).one(inserted.getId()); + assertNotNull(foundInserted, "inserted personValue not found"); + assertEquals(inserted, foundInserted); + + // upsert will insert + couchbaseTemplate.removeById().one(inserted.getId()); + upserted = couchbaseTemplate.upsertById(PersonValue.class).one(inserted); + assertNotEquals(0, upserted.getVersion()); + PersonValue foundUpserted = couchbaseTemplate.findById(PersonValue.class).one(upserted.getId()); + assertNotNull(foundUpserted, "upserted personValue not found"); + assertEquals(upserted, foundUpserted); + + // upsert will replace + upserted = couchbaseTemplate.upsertById(PersonValue.class).one(inserted); + assertNotEquals(0, upserted.getVersion()); + PersonValue foundUpserted2 = couchbaseTemplate.findById(PersonValue.class).one(upserted.getId()); + assertNotNull(foundUpserted2, "upserted personValue not found"); + assertEquals(upserted, foundUpserted2); + + replaced = couchbaseTemplate.replaceById(PersonValue.class).one(upserted); + assertNotEquals(0, replaced.getVersion()); + PersonValue foundReplaced = couchbaseTemplate.findById(PersonValue.class).one(replaced.getId()); + assertNotNull(foundReplaced, "replaced personValue not found"); + assertEquals(replaced, foundReplaced); - inserted = couchbaseTemplate.insertById(PersonValue.class).one(personValue); - assertNotEquals(0, inserted.getVersion()); - PersonValue foundInserted = couchbaseTemplate.findById(PersonValue.class).one(inserted.getId()); - assertNotNull(foundInserted, "inserted personValue not found"); - assertEquals(inserted, foundInserted); - - // upsert will be inserted - couchbaseTemplate.removeById().one(inserted.getId()); - upserted = couchbaseTemplate.upsertById(PersonValue.class).one(inserted); - assertNotEquals(0, upserted.getVersion()); - PersonValue foundUpserted = couchbaseTemplate.findById(PersonValue.class).one(upserted.getId()); - assertNotNull(foundUpserted, "upserted personValue not found"); - assertEquals(upserted, foundUpserted); - - // upsert will be replaced - upserted = couchbaseTemplate.upsertById(PersonValue.class).one(inserted); - assertNotEquals(0, upserted.getVersion()); - PersonValue foundUpserted2 = couchbaseTemplate.findById(PersonValue.class).one(upserted.getId()); - assertNotNull(foundUpserted2, "upserted personValue not found"); - assertEquals(upserted, foundUpserted2); - - replaced = couchbaseTemplate.replaceById(PersonValue.class).one(upserted); - assertNotEquals(0, replaced.getVersion()); - PersonValue foundReplaced = couchbaseTemplate.findById(PersonValue.class).one(replaced.getId()); - assertNotNull(foundReplaced, "replaced personValue not found"); - assertEquals(replaced, foundReplaced); - - } finally { - couchbaseTemplate.removeById().one(inserted.getId()); - } } private void sleepSecs(int i) { diff --git a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryIntegrationTests.java index 33c5c0241..15fcb6e62 100644 --- a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryIntegrationTests.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.UUID; +import com.couchbase.client.java.manager.query.CreatePrimaryQueryIndexOptions; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -65,12 +66,8 @@ class CouchbaseTemplateQueryIntegrationTests extends ClusterAwareIntegrationTest @BeforeAll static void beforeAll() { couchbaseClientFactory = new SimpleCouchbaseClientFactory(connectionString(), authenticator(), bucketName()); - - try { - couchbaseClientFactory.getCluster().queryIndexes().createPrimaryIndex(bucketName()); - } catch (IndexExistsException ex) { - // ignore, all good. - } + couchbaseClientFactory.getCluster().queryIndexes().createPrimaryIndex(bucketName(), + CreatePrimaryQueryIndexOptions.createPrimaryQueryIndexOptions().ignoreIfExists(true)); } @AfterAll @@ -177,8 +174,7 @@ void removeByMatchingQuery() { Query nonSpecialUsers = new Query(QueryCriteria.where("firstname").notLike("special")); couchbaseTemplate.removeByQuery(User.class).consistentWith(QueryScanConsistency.REQUEST_PLUS) - .matching(nonSpecialUsers) - .all(); + .matching(nonSpecialUsers).all(); assertNull(couchbaseTemplate.findById(User.class).one(user1.getId())); assertNull(couchbaseTemplate.findById(User.class).one(user2.getId())); diff --git a/src/test/java/org/springframework/data/couchbase/domain/UserAnnotated.java b/src/test/java/org/springframework/data/couchbase/domain/UserAnnotated.java index 30c3b06c6..ef82c2ff3 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/UserAnnotated.java +++ b/src/test/java/org/springframework/data/couchbase/domain/UserAnnotated.java @@ -16,19 +16,10 @@ package org.springframework.data.couchbase.domain; -import java.util.Objects; - -import org.springframework.data.annotation.CreatedBy; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.LastModifiedBy; -import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.annotation.PersistenceConstructor; -import org.springframework.data.annotation.Version; import org.springframework.data.couchbase.core.mapping.Document; /** - * Annoted User entity for tests + * Annotated User entity for tests * * @author Michael Reiche */ diff --git a/src/test/java/org/springframework/data/couchbase/domain/UserAnnotated2.java b/src/test/java/org/springframework/data/couchbase/domain/UserAnnotated2.java new file mode 100644 index 000000000..72d05a549 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/domain/UserAnnotated2.java @@ -0,0 +1,38 @@ +/* + * Copyright 2020 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.couchbase.domain; + +import java.util.concurrent.TimeUnit; + +import org.springframework.data.couchbase.core.mapping.Document; + +/** + * Annotated User entity for tests + * + * @author Michael Reiche + */ + +@Document(expiryExpression = "${myExpiryExpression}", expiryUnit = TimeUnit.SECONDS) +public class UserAnnotated2 extends User { + static { + System.setProperty("myExpiryExpression", "2"); + } + + public UserAnnotated2(String id, String firstname, String lastname) { + super(id, firstname, lastname); + } +} 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 4c14ce1d2..09bce76e6 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java @@ -30,6 +30,7 @@ import java.util.concurrent.Future; import java.util.stream.Collectors; +import com.couchbase.client.java.manager.query.CreatePrimaryQueryIndexOptions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -74,11 +75,8 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr @BeforeEach void beforeEach() { - try { - clientFactory.getCluster().queryIndexes().createPrimaryIndex(bucketName()); - } catch (IndexExistsException ex) { - // ignore, all good. - } + clientFactory.getCluster().queryIndexes().createPrimaryIndex(bucketName(), + CreatePrimaryQueryIndexOptions.createPrimaryQueryIndexOptions().ignoreIfExists(true)); } @Test 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 2e0ed241d..66b659fc2 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java @@ -22,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import com.couchbase.client.java.manager.query.CreatePrimaryQueryIndexOptions; import reactor.core.publisher.Flux; import reactor.test.StepVerifier; @@ -70,11 +71,8 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends ClusterAwa @BeforeEach void beforeEach() { - try { - clientFactory.getCluster().queryIndexes().createPrimaryIndex(bucketName()); - } catch (IndexExistsException ex) { - // ignore, all good. - } + clientFactory.getCluster().queryIndexes().createPrimaryIndex(bucketName(), + CreatePrimaryQueryIndexOptions.createPrimaryQueryIndexOptions().ignoreIfExists(true)); } @Test