From cf820ce05abb96fb930d0f943ceeb332d103537e Mon Sep 17 00:00:00 2001 From: mikereiche Date: Thu, 14 Jan 2021 18:07:56 -0800 Subject: [PATCH] DATACOUCH-1041 - Add withCas() method to RemoveById. Add withCas() method to RemoveById. --- .../core/ExecutableRemoveByIdOperation.java | 7 +++- .../ExecutableRemoveByIdOperationSupport.java | 21 +++++++--- .../core/ReactiveRemoveByIdOperation.java | 9 ++++- .../ReactiveRemoveByIdOperationSupport.java | 26 +++++++++---- ...hbaseTemplateKeyValueIntegrationTests.java | 39 ++++++++++++++----- 5 files changed, 76 insertions(+), 26 deletions(-) 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 a53a244ba..6918bdf3f 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByIdOperation.java @@ -50,6 +50,11 @@ interface RemoveByIdWithDurability extends RemoveByIdWithCollection, WithDurabil } - interface ExecutableRemoveById extends RemoveByIdWithDurability {} + interface RemoveByIdWithCas extends RemoveByIdWithDurability { + + RemoveByIdWithDurability withCas(Long cas); + } + + interface ExecutableRemoveById extends RemoveByIdWithCas {} } 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 3d1f10778..bbdaf593c 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByIdOperationSupport.java @@ -35,7 +35,8 @@ public ExecutableRemoveByIdOperationSupport(final CouchbaseTemplate template) { @Override public ExecutableRemoveById removeById() { - return new ExecutableRemoveByIdSupport(template, null, PersistTo.NONE, ReplicateTo.NONE, DurabilityLevel.NONE); + return new ExecutableRemoveByIdSupport(template, null, PersistTo.NONE, ReplicateTo.NONE, DurabilityLevel.NONE, + null); } static class ExecutableRemoveByIdSupport implements ExecutableRemoveById { @@ -45,17 +46,19 @@ static class ExecutableRemoveByIdSupport implements ExecutableRemoveById { private final PersistTo persistTo; private final ReplicateTo replicateTo; private final DurabilityLevel durabilityLevel; + private final Long cas; private final ReactiveRemoveByIdSupport reactiveRemoveByIdSupport; ExecutableRemoveByIdSupport(final CouchbaseTemplate template, final String collection, final PersistTo persistTo, - final ReplicateTo replicateTo, final DurabilityLevel durabilityLevel) { + final ReplicateTo replicateTo, final DurabilityLevel durabilityLevel, Long cas) { this.template = template; this.collection = collection; this.persistTo = persistTo; this.replicateTo = replicateTo; this.durabilityLevel = durabilityLevel; + this.cas = cas; this.reactiveRemoveByIdSupport = new ReactiveRemoveByIdSupport(template.reactive(), collection, persistTo, - replicateTo, durabilityLevel); + replicateTo, durabilityLevel, cas); } @Override @@ -71,20 +74,26 @@ public List all(final Collection ids) { @Override public TerminatingRemoveById inCollection(final String collection) { Assert.hasText(collection, "Collection must not be null nor empty."); - return new ExecutableRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel); + return new ExecutableRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel, null); } @Override public RemoveByIdWithCollection withDurability(final DurabilityLevel durabilityLevel) { Assert.notNull(durabilityLevel, "Durability Level must not be null."); - return new ExecutableRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel); + return new ExecutableRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel, null); } @Override public RemoveByIdWithCollection 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, collection, persistTo, replicateTo, durabilityLevel); + return new ExecutableRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel, null); + } + + @Override + public RemoveByIdWithCas withCas(final Long cas) { + Assert.notNull(cas, "CAS must not be null."); + return new ExecutableRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel, cas); } } 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 101970acf..e8af89b3f 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByIdOperation.java @@ -42,6 +42,7 @@ interface TerminatingRemoveById extends OneAndAllIdReactive { interface RemoveByIdWithCollection extends TerminatingRemoveById, WithCollection { TerminatingRemoveById inCollection(String collection); + } interface RemoveByIdWithDurability extends RemoveByIdWithCollection, WithDurability { @@ -52,6 +53,12 @@ interface RemoveByIdWithDurability extends RemoveByIdWithCollection, WithDurabil } - interface ReactiveRemoveById extends RemoveByIdWithDurability {} + interface RemoveByIdWithCas extends RemoveByIdWithDurability { + + RemoveByIdWithDurability withCas(Long cas); + + } + + interface ReactiveRemoveById extends RemoveByIdWithCas {} } 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 f519d0cea..4b33c7437 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByIdOperationSupport.java @@ -37,7 +37,7 @@ public ReactiveRemoveByIdOperationSupport(final ReactiveCouchbaseTemplate templa @Override public ReactiveRemoveById removeById() { - return new ReactiveRemoveByIdSupport(template, null, PersistTo.NONE, ReplicateTo.NONE, DurabilityLevel.NONE); + return new ReactiveRemoveByIdSupport(template, null, PersistTo.NONE, ReplicateTo.NONE, DurabilityLevel.NONE, null); } static class ReactiveRemoveByIdSupport implements ReactiveRemoveById { @@ -47,14 +47,16 @@ static class ReactiveRemoveByIdSupport implements ReactiveRemoveById { private final PersistTo persistTo; private final ReplicateTo replicateTo; private final DurabilityLevel durabilityLevel; + private final Long cas; ReactiveRemoveByIdSupport(final ReactiveCouchbaseTemplate template, final String collection, - final PersistTo persistTo, final ReplicateTo replicateTo, final DurabilityLevel durabilityLevel) { + final PersistTo persistTo, final ReplicateTo replicateTo, final DurabilityLevel durabilityLevel, Long cas) { this.template = template; this.collection = collection; this.persistTo = persistTo; this.replicateTo = replicateTo; this.durabilityLevel = durabilityLevel; + this.cas = cas; } @Override @@ -81,28 +83,36 @@ private RemoveOptions buildRemoveOptions() { } else if (durabilityLevel != DurabilityLevel.NONE) { options.durability(durabilityLevel); } + if (cas != null) { + options.cas(cas); + } return options; } + @Override + public RemoveByIdWithDurability inCollection(final String collection) { + Assert.hasText(collection, "Collection must not be null nor empty."); + return new ReactiveRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel, null); + } + @Override public RemoveByIdWithCollection withDurability(final DurabilityLevel durabilityLevel) { Assert.notNull(durabilityLevel, "Durability Level must not be null."); - return new ReactiveRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel); + return new ReactiveRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel, null); } @Override public RemoveByIdWithCollection 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, collection, persistTo, replicateTo, durabilityLevel); + return new ReactiveRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel, null); } @Override - public RemoveByIdWithDurability inCollection(final String collection) { - Assert.hasText(collection, "Collection must not be null nor empty."); - return new ReactiveRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel); + public RemoveByIdWithDurability withCas(final Long cas) { + Assert.notNull(cas, "CAS must not be null."); + return new ReactiveRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel, cas); } - } } 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 868455030..9d4940d8a 100644 --- a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java @@ -45,10 +45,10 @@ import org.springframework.data.couchbase.domain.UserAnnotated3; import org.springframework.data.couchbase.util.ClusterType; import org.springframework.data.couchbase.util.IgnoreWhen; +import org.springframework.data.couchbase.util.JavaIntegrationTests; import com.couchbase.client.java.kv.PersistTo; import com.couchbase.client.java.kv.ReplicateTo; -import org.springframework.data.couchbase.util.JavaIntegrationTests; ; @@ -103,7 +103,8 @@ void withDurability() "firstname", "lastname"); if (clazz.equals(User.class)) { // User.java doesn't have an durability annotation - operator = (OneAndAllEntity) ((WithDurability) operator).withDurability(PersistTo.ACTIVE, ReplicateTo.NONE); + operator = (OneAndAllEntity) ((WithDurability) operator).withDurability(PersistTo.ACTIVE, + ReplicateTo.NONE); } // if replace, we need to insert a document to replace @@ -194,16 +195,34 @@ void upsertAndReplaceById() { @Test void upsertAndRemoveById() { - User user = new User(UUID.randomUUID().toString(), "firstname", "lastname"); - User modified = couchbaseTemplate.upsertById(User.class).one(user); - assertEquals(user, modified); + { + User user = new User(UUID.randomUUID().toString(), "firstname", "lastname"); + User modified = couchbaseTemplate.upsertById(User.class).one(user); + assertEquals(user, modified); - RemoveResult removeResult = couchbaseTemplate.removeById().one(user.getId()); - assertEquals(user.getId(), removeResult.getId()); - assertTrue(removeResult.getCas() != 0); - assertTrue(removeResult.getMutationToken().isPresent()); + RemoveResult removeResult = couchbaseTemplate.removeById().one(user.getId()); + assertEquals(user.getId(), removeResult.getId()); + assertTrue(removeResult.getCas() != 0); + assertTrue(removeResult.getMutationToken().isPresent()); - assertNull(couchbaseTemplate.findById(User.class).one(user.getId())); + assertNull(couchbaseTemplate.findById(User.class).one(user.getId())); + } + { + User user = new User(UUID.randomUUID().toString(), "firstname", "lastname"); + User modified = couchbaseTemplate.upsertById(User.class).one(user); + System.out.println(reactiveCouchbaseTemplate.support().getCas(user)); + System.out.println(reactiveCouchbaseTemplate.support().getCas(modified)); + assertEquals(user, modified); + + // careful now - user and modified are the same object. The object has the new cas (@Version version) + Long savedCas = modified.getVersion(); + modified.setVersion(123); + assertThrows(DataIntegrityViolationException.class, () -> couchbaseTemplate.removeById() + .withCas(reactiveCouchbaseTemplate.support().getCas(modified)).one(modified.getId())); + modified.setVersion(savedCas); + couchbaseTemplate.removeById().withCas(reactiveCouchbaseTemplate.support().getCas(modified)) + .one(modified.getId()); + } } @Test