diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc
index 7d063b6ab..eacddaa97 100644
--- a/src/main/asciidoc/new-features.adoc
+++ b/src/main/asciidoc/new-features.adoc
@@ -7,7 +7,7 @@ This section briefly covers items that are new and noteworthy in the latest rele
== New in Spring Data Redis 2.6
* Support for `SubscriptionListener` when using `MessageListener` for subscription confirmation callbacks. `ReactiveRedisMessageListenerContainer` and `ReactiveRedisOperations` provide `receiveLater(…)` and `listenToLater(…)` methods to await until Redis acknowledges the subscription.
-* Support Redis 6.2 commands (`LPOP`/`RPOP` with `count`).
+* Support Redis 6.2 commands (`LPOP`/`RPOP` with `count`, `COPY`).
[[new-in-2.5.0]]
== New in Spring Data Redis 2.5
diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java
index d5d5387e2..6ce7ac173 100644
--- a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java
@@ -241,6 +241,15 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
delegate.close();
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[], boolean)
+ */
+ @Override
+ public Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace) {
+ return convertAndReturn(delegate.copy(sourceKey, targetKey, replace), Converters.identityConverter());
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisServerCommands#dbSize()
@@ -268,6 +277,7 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
return convertAndReturn(delegate.decrBy(key, value), Converters.identityConverter());
}
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#del(byte[][])
@@ -277,15 +287,6 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
return convertAndReturn(delegate.del(keys), Converters.identityConverter());
}
- /*
- * (non-Javadoc)
- * @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
- */
- @Override
- public Boolean copy(byte[] sourceKey, byte[] targetKey) {
- return convertAndReturn(delegate.copy(sourceKey, targetKey), Converters.identityConverter());
- }
-
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])
@@ -1900,6 +1901,14 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
return convertAndReturn(delegate.bRPopLPush(timeout, serialize(srcKey), serialize(dstKey)), bytesToString);
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.StringRedisConnection#copy(java.lang.String, java.lang.String, boolean)
+ */
+ @Override
+ public Boolean copy(String sourceKey, String targetKey, boolean replace) {
+ return copy(serialize(sourceKey), serialize(targetKey), replace);
+ }
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#decr(java.lang.String)
@@ -1927,14 +1936,6 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
return del(serializeMulti(keys));
}
- /*
- * (non-Javadoc)
- * @see org.springframework.data.redis.connection.StringRedisConnection#copy(java.lang.String[])
- */
- @Override
- public Boolean copy(String sourceKey, String targetKey) {
- return copy(serialize(sourceKey), serialize(targetKey));
- }
/*
* (non-Javadoc)
diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
index c963dc25b..f26ba14cd 100644
--- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
@@ -64,6 +64,13 @@ public interface DefaultedRedisConnection extends RedisConnection {
// KEY COMMANDS
+ /** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
+ @Override
+ @Deprecated
+ default Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace) {
+ return keyCommands().copy(sourceKey, targetKey, replace);
+ }
+
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
@Override
@Deprecated
@@ -85,13 +92,6 @@ public interface DefaultedRedisConnection extends RedisConnection {
return keyCommands().del(keys);
}
- /** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
- @Override
- @Deprecated
- default Boolean copy(byte[] sourceKey, byte[] targetKey) {
- return keyCommands().copy(sourceKey, targetKey);
- }
-
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
@Override
@Deprecated
diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java
index 9eea37b00..bac73b442 100644
--- a/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java
@@ -43,6 +43,124 @@ import org.springframework.util.Assert;
*/
public interface ReactiveKeyCommands {
+ /**
+ * {@code MOVE} command parameters.
+ *
+ * @author Mark Paluch
+ * @see Redis Documentation: MOVE
+ */
+ class CopyCommand extends KeyCommand {
+
+ private final @Nullable ByteBuffer target;
+ private final boolean replace;
+ private final @Nullable Integer database;
+
+ public CopyCommand(ByteBuffer key, @Nullable ByteBuffer target, boolean replace, @Nullable Integer database) {
+ super(key);
+ this.target = target;
+ this.replace = replace;
+ this.database = database;
+ }
+
+ /**
+ * Creates a new {@link CopyCommand} given a {@link ByteBuffer key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return a new {@link CopyCommand} for {@link ByteBuffer key}.
+ */
+ public static CopyCommand key(ByteBuffer key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return new CopyCommand(key, null, false, null);
+ }
+
+ /**
+ * Applies the {@link ByteBuffer targetKey}. Constructs a new command instance with all previously configured
+ * properties.
+ *
+ * @param targetKey must not be {@literal null}.
+ * @return a new {@link CopyCommand} with {@literal database} applied.
+ */
+ public CopyCommand to(ByteBuffer targetKey) {
+
+ Assert.notNull(targetKey, "Key must not be null!");
+
+ return new CopyCommand(getKey(), targetKey, isReplace(), database);
+ }
+
+ /**
+ * Applies {@code replace}. Constructs a new command instance with all previously configured properties.
+ *
+ * @param key must not be {@literal null}.
+ * @return a new {@link CopyCommand} with {@literal replace} applied.
+ */
+ public CopyCommand replace(boolean replace) {
+ return new CopyCommand(getKey(), this.target, replace, database);
+ }
+
+ /**
+ * Applies the {@literal database} index. Constructs a new command instance with all previously configured
+ * properties.
+ *
+ * @param database
+ * @return a new {@link CopyCommand} with {@literal database} applied.
+ */
+ public CopyCommand database(int database) {
+ return new CopyCommand(getKey(), this.target, isReplace(), database);
+ }
+
+ /**
+ * @return can be {@literal null}.
+ */
+ @Nullable
+ public ByteBuffer getTarget() {
+ return target;
+ }
+
+ public boolean isReplace() {
+ return replace;
+ }
+
+ /**
+ * @return can be {@literal null}.
+ */
+ @Nullable
+ public Integer getDatabase() {
+ return database;
+ }
+
+ }
+
+ /**
+ * Copy given {@code key} to a target {@code key}.
+ *
+ * @param sourceKey must not be {@literal null}.
+ * @param targetKey must not be {@literal null}.
+ * @param replace whether to replace existing keys.
+ * @return
+ * @see Redis Documentation: COPY
+ * @since 2.6
+ */
+ default Mono copy(ByteBuffer sourceKey, ByteBuffer targetKey, boolean replace) {
+
+ Assert.notNull(sourceKey, "Source key must not be null!");
+ Assert.notNull(targetKey, "Targetk ey must not be null!");
+
+ return copy(Mono.just(CopyCommand.key(sourceKey).to(targetKey).replace(replace))).next()
+ .map(BooleanResponse::getOutput);
+ }
+
+ /**
+ * Copy keys one-by-one.
+ *
+ * @param commands must not be {@literal null}.
+ * @return {@link Flux} of {@link BooleanResponse} holding the {@literal key} to move along with the copy result.
+ * @see Redis Documentation: COPY
+ * @since 2.6
+ */
+ Flux> copy(Publisher commands);
+
/**
* Determine if given {@literal key} exists.
*
@@ -670,7 +788,7 @@ public interface ReactiveKeyCommands {
* Creates a new {@link MoveCommand} given a {@link ByteBuffer key}.
*
* @param key must not be {@literal null}.
- * @return a new {@link ExpireCommand} for {@link ByteBuffer key}.
+ * @return a new {@link MoveCommand} for {@link ByteBuffer key}.
*/
public static MoveCommand key(ByteBuffer key) {
diff --git a/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java
index 908c6ed60..d13c18e28 100644
--- a/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java
@@ -35,6 +35,19 @@ import org.springframework.util.Assert;
*/
public interface RedisKeyCommands {
+ /**
+ * Copy given {@code sourceKey} to {@code targetKey}.
+ *
+ * @param sourceKey must not be {@literal null}.
+ * @param targetKey must not be {@literal null}.
+ * @param replace whether to replace existing keys.
+ * @return {@literal null} when used in pipeline / transaction.
+ * @see Redis Documentation: COPY
+ * @since 2.6
+ */
+ @Nullable
+ Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace);
+
/**
* Determine if given {@code key} exists.
*
@@ -72,17 +85,6 @@ public interface RedisKeyCommands {
@Nullable
Long del(byte[]... keys);
- /**
- * Copy given {@code sourceKey} to {@code targetKey}.
- *
- * @param sourceKey must not be {@literal null}.
- * @param targetKey must not be {@literal null}.
- * @return
- * @see Redis Documentation: COPY
- */
- @Nullable
- Boolean copy(byte[] sourceKey, byte[] targetKey);
-
/**
* Unlink the {@code keys} from the keyspace. Unlike with {@link #del(byte[]...)} the actual memory reclaiming here
* happens asynchronously.
diff --git a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java
index 261921e90..66c9f2793 100644
--- a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java
@@ -139,11 +139,12 @@ public interface StringRedisConnection extends RedisConnection {
*
* @param sourceKey must not be {@literal null}.
* @param targetKey must not be {@literal null}.
- * @return
+ * @param replace whether to replace existing keys.
+ * @return {@literal null} when used in pipeline / transaction.
* @see Redis Documentation: COPY
* @see RedisKeyCommands#copy(byte[], byte[])
*/
- Boolean copy(String sourceKey, String targetKey);
+ Boolean copy(String sourceKey, String targetKey, boolean replace);
/**
* Unlink the {@code keys} from the keyspace. Unlike with {@link #del(String...)} the actual memory reclaiming here
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java
index a78937187..9965552e3 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java
@@ -64,6 +64,19 @@ class JedisClusterKeyCommands implements RedisKeyCommands {
this.connection = connection;
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
+ */
+ @Override
+ public Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace) {
+
+ Assert.notNull(sourceKey, "source key must not be null!");
+ Assert.notNull(targetKey, "target key must not be null!");
+
+ return connection.getCluster().copy(sourceKey, targetKey, replace);
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#del(byte[][])
@@ -88,18 +101,6 @@ class JedisClusterKeyCommands implements RedisKeyCommands {
.resultsAsList().size();
}
- /*
- * (non-Javadoc)
- * @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
- */
- @Override
- public Boolean copy(byte[] sourceKey, byte[] targetKey) {
- Assert.notNull(sourceKey, "source key must not be null!");
- Assert.notNull(targetKey, "target key must not be null!");
-
- return connection.getCluster().copy(sourceKey, targetKey, false);
- }
-
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java
index a78b99aca..e7acd67ef 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java
@@ -95,11 +95,12 @@ class JedisKeyCommands implements RedisKeyCommands {
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
*/
- public Boolean copy(byte[] sourceKey, byte[] targetKey) {
+ public Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace) {
+
Assert.notNull(sourceKey, "source key must not be null!");
Assert.notNull(targetKey, "target key must not be null!");
- return connection.invoke().just(BinaryJedis::copy, MultiKeyPipelineBase::copy, sourceKey, targetKey, false);
+ return connection.invoke().just(BinaryJedis::copy, MultiKeyPipelineBase::copy, sourceKey, targetKey, replace);
}
/*
diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceKeyCommands.java
index 3ff5da5f0..f09d2683a 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceKeyCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceKeyCommands.java
@@ -15,6 +15,7 @@
*/
package org.springframework.data.redis.connection.lettuce;
+import io.lettuce.core.CopyArgs;
import io.lettuce.core.KeyScanCursor;
import io.lettuce.core.RestoreArgs;
import io.lettuce.core.ScanArgs;
@@ -52,6 +53,20 @@ class LettuceKeyCommands implements RedisKeyCommands {
this.connection = connection;
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
+ */
+ @Override
+ public Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace) {
+
+ Assert.notNull(sourceKey, "source key must not be null!");
+ Assert.notNull(targetKey, "target key must not be null!");
+
+ return connection.invoke().just(RedisKeyAsyncCommands::copy, sourceKey, targetKey,
+ CopyArgs.Builder.replace(replace));
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#exists(byte[])
@@ -91,17 +106,6 @@ class LettuceKeyCommands implements RedisKeyCommands {
return connection.invoke().just(RedisKeyAsyncCommands::del, keys);
}
- /*
- * (non-Javadoc)
- * @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
- */
- @Override
- public Boolean copy(byte[] sourceKey, byte[] targetKey) {
- Assert.notNull(sourceKey, "source key must not be null!");
- Assert.notNull(targetKey, "target key must not be null!");
-
- return connection.invoke().just(RedisKeyAsyncCommands::copy, targetKey, sourceKey);
- }
/*
* (non-Javadoc)
diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java
index c04d957a4..d6bb1c337 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java
@@ -15,6 +15,7 @@
*/
package org.springframework.data.redis.connection.lettuce;
+import io.lettuce.core.CopyArgs;
import io.lettuce.core.ScanStream;
import io.lettuce.core.api.reactive.RedisKeyReactiveCommands;
import reactor.core.publisher.Flux;
@@ -59,6 +60,27 @@ class LettuceReactiveKeyCommands implements ReactiveKeyCommands {
this.connection = connection;
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#copy(org.reactivestreams.Publisher)
+ */
+ @Override
+ public Flux> copy(Publisher commands) {
+
+ return connection.execute(cmd -> Flux.from(commands).concatMap((command) -> {
+
+ Assert.notNull(command.getKey(), "Key must not be null!");
+
+ CopyArgs copyArgs = CopyArgs.Builder.replace(command.isReplace());
+ if (command.getDatabase() != null) {
+ copyArgs.destinationDb(command.getDatabase());
+ }
+
+ return cmd.copy(command.getKey(), command.getTarget(), copyArgs)
+ .map((value) -> new BooleanResponse<>(command, value));
+ }));
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#exists(org.reactivestreams.Publisher)
diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java
index b2b7ada82..ad0f204d5 100644
--- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java
@@ -163,6 +163,17 @@ public interface ReactiveRedisOperations {
// Methods dealing with Redis Keys
// -------------------------------------------------------------------------
+ /**
+ * Copy given {@code sourceKey} to {@code targetKey}.
+ *
+ * @param sourceKey must not be {@literal null}.
+ * @param targetKey must not be {@literal null}.
+ * @return
+ * @see Redis Documentation: COPY
+ * @since 2.6
+ */
+ Mono copy(K sourceKey, K targetKey, boolean replace);
+
/**
* Determine if given {@code key} exists.
*
diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java
index c9cf241c0..fba47e095 100644
--- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java
+++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java
@@ -256,6 +256,19 @@ public class ReactiveRedisTemplate implements ReactiveRedisOperations copy(K sourceKey, K targetKey, boolean replace) {
+
+ Assert.notNull(sourceKey, "Source key must not be null!");
+ Assert.notNull(targetKey, "Target key must not be null!");
+
+ return createMono(connection -> connection.keyCommands().copy(rawKey(sourceKey), rawKey(targetKey), replace));
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveRedisOperations#hasKey(java.lang.Object)
diff --git a/src/main/java/org/springframework/data/redis/core/RedisOperations.java b/src/main/java/org/springframework/data/redis/core/RedisOperations.java
index 5ac17f9e0..fa03490f5 100644
--- a/src/main/java/org/springframework/data/redis/core/RedisOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/RedisOperations.java
@@ -158,6 +158,19 @@ public interface RedisOperations {
// Methods dealing with Redis Keys
// -------------------------------------------------------------------------
+ /**
+ * Copy given {@code sourceKey} to {@code targetKey}.
+ *
+ * @param sourceKey must not be {@literal null}.
+ * @param targetKey must not be {@literal null}.
+ * @param replace whether the key was copied. {@literal null} when used in pipeline / transaction.
+ * @return
+ * @see Redis Documentation: COPY
+ * @since 2.6
+ */
+ @Nullable
+ Boolean copy(K sourceKey, K targetKey, boolean replace);
+
/**
* Determine if given {@code key} exists.
*
@@ -180,6 +193,7 @@ public interface RedisOperations {
@Nullable
Long countExistingKeys(Collection keys);
+
/**
* Delete given {@code key}.
*
@@ -200,17 +214,6 @@ public interface RedisOperations {
@Nullable
Long delete(Collection keys);
- /**
- * Copy given {@code sourceKey} to {@code targetKey}.
- *
- * @param sourceKey must not be {@literal null}.
- * @param targetKey must not be {@literal null}.
- * @return
- * @see Redis Documentation: COPY
- */
- @Nullable
- Boolean copy(K sourceKey, K targetKey);
-
/**
* Unlink the {@code key} from the keyspace. Unlike with {@link #delete(Object)} the actual memory reclaiming here
* happens asynchronously.
diff --git a/src/main/java/org/springframework/data/redis/core/RedisTemplate.java b/src/main/java/org/springframework/data/redis/core/RedisTemplate.java
index 6448a00fc..c6a58a55c 100644
--- a/src/main/java/org/springframework/data/redis/core/RedisTemplate.java
+++ b/src/main/java/org/springframework/data/redis/core/RedisTemplate.java
@@ -698,6 +698,19 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation
return raw == null ? Collections.emptyList() : raw;
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.RedisOperations#delete(java.lang.Object, java.lang.Object, boolean)
+ */
+ @Override
+ public Boolean copy(K source, K target, boolean replace) {
+
+ byte[] sourceKey = rawKey(source);
+ byte[] targetKey = rawKey(target);
+
+ return execute(connection -> connection.copy(sourceKey, targetKey, replace), true);
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.RedisOperations#delete(java.lang.Object)
@@ -708,18 +721,9 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation
byte[] rawKey = rawKey(key);
Long result = execute(connection -> connection.del(rawKey), true);
-
return result != null && result.intValue() == 1;
}
- @Override
- public Boolean copy(K source, K target) {
- byte[] sourceKey = rawKey(source);
- byte[] targetKey = rawKey(target);
-
- return execute(connection -> connection.copy(sourceKey, targetKey), true);
- }
-
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.RedisOperations#delete(java.util.Collection)
diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java
index 4c6485fb4..23f741034 100644
--- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java
+++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java
@@ -509,6 +509,18 @@ public abstract class AbstractConnectionIntegrationTests {
.isThrownBy(() -> connection.bitOp(BitOperation.NOT, "key3", "key1", "key2"));
}
+ @Test
+ @EnabledOnCommand("COPY")
+ public void testCopy() {
+
+ actual.add(connection.set("foo", "bar"));
+ actual.add(connection.copy("foo", "baz", false));
+
+ verifyResults(Arrays.asList(true, true));
+ assertThat(connection.get("baz")).isEqualTo("bar");
+ assertThat(connection.exists("foo")).isTrue();
+ }
+
@Test
void testInfo() {
diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java
index d7bc4474a..2691e66c4 100644
--- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java
+++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java
@@ -100,6 +100,12 @@ public class DefaultStringRedisConnectionPipelineTxTests extends DefaultStringRe
super.testBrPopLPush();
}
+ @Test
+ public void testCopy() {
+ doReturn(Collections.singletonList(Arrays.asList(Boolean.TRUE))).when(nativeConnection).closePipeline();
+ super.testCopy();
+ }
+
@Test
public void testDbSize() {
doReturn(Collections.singletonList(Collections.singletonList(3L))).when(nativeConnection).closePipeline();
@@ -142,13 +148,6 @@ public class DefaultStringRedisConnectionPipelineTxTests extends DefaultStringRe
super.testDel();
}
- @Test
- public void testCopy() {
- doReturn(Collections.singletonList(Collections.singletonList(Boolean.TRUE))).when(nativeConnection).closePipeline();
- super.testCopy();
- }
-
-
@Test
public void testEchoBytes() {
doReturn(Collections.singletonList(Arrays.asList(new Object[] { barBytes }))).when(nativeConnection)
diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java
index e68204866..fe09f3610 100644
--- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java
+++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java
@@ -187,6 +187,13 @@ public class DefaultStringRedisConnectionTests {
verifyResults(Collections.singletonList(bar));
}
+ @Test
+ public void testCopy() {
+ doReturn(Boolean.TRUE).when(nativeConnection).copy(fooBytes, barBytes, false);
+ actual.add(connection.copy(foo, bar, false));
+ verifyResults(Collections.singletonList(Boolean.TRUE));
+ }
+
@Test
public void testDbSize() {
doReturn(3L).when(nativeConnection).dbSize();
@@ -236,13 +243,6 @@ public class DefaultStringRedisConnectionTests {
verifyResults(Collections.singletonList(1L));
}
- @Test
- public void testCopy() {
- doReturn(Boolean.TRUE).when(nativeConnection).copy(fooBytes, barBytes);
- actual.add(connection.copy(foo, bar));
- verifyResults(Collections.singletonList(Boolean.TRUE));
- }
-
@Test
public void testEchoBytes() {
doReturn(barBytes).when(nativeConnection).echo(fooBytes);
diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java
index c1ef9034c..c846f0bb2 100644
--- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java
+++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java
@@ -92,6 +92,12 @@ public class DefaultStringRedisConnectionTxTests extends DefaultStringRedisConne
super.testBrPopLPush();
}
+ @Test
+ public void testCopy() {
+ doReturn(Collections.singletonList(Boolean.TRUE)).when(nativeConnection).exec();
+ super.testCopy();
+ }
+
@Test
public void testDbSize() {
doReturn(Collections.singletonList(3L)).when(nativeConnection).exec();
diff --git a/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java
index 318549d09..019f0f491 100644
--- a/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java
+++ b/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java
@@ -187,8 +187,8 @@ class RedisConnectionUnitTests {
return delegate.del(keys);
}
- public Boolean copy(byte[] sourceKey, byte[] targetKey) {
- return delegate.copy(sourceKey, targetKey);
+ public Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace) {
+ return delegate.copy(sourceKey, targetKey, replace);
}
public void close() throws DataAccessException {
diff --git a/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java
index 7b127b4aa..f84d60a4e 100644
--- a/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java
+++ b/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java
@@ -53,6 +53,7 @@ import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.data.redis.test.condition.EnabledIfLongRunningTest;
+import org.springframework.data.redis.test.condition.EnabledOnCommand;
import org.springframework.data.redis.test.extension.parametrized.MethodSource;
import org.springframework.data.redis.test.extension.parametrized.ParameterizedRedisTest;
@@ -91,6 +92,34 @@ public class ReactiveRedisTemplateIntegrationTests {
connection.close();
}
+ @ParameterizedRedisTest // GH-2040
+ @EnabledOnCommand("COPY")
+ void copy() {
+
+ ReactiveRedisClusterConnection connection = null;
+ try {
+ connection = redisTemplate.getConnectionFactory().getReactiveClusterConnection();
+ assumeThat(connection == null).isTrue();
+ } catch (InvalidDataAccessApiUsageException e) {} finally {
+ if (connection != null) {
+ connection.close();
+ }
+ }
+
+ K key = keyFactory.instance();
+ K targetKey = keyFactory.instance();
+ V value = valueFactory.instance();
+ V nextValue = valueFactory.instance();
+
+ redisTemplate.opsForValue().set(key, value).as(StepVerifier::create).expectNext(true).verifyComplete();
+ redisTemplate.copy(key, targetKey, false).as(StepVerifier::create).expectNext(true).verifyComplete();
+ redisTemplate.opsForValue().get(targetKey).as(StepVerifier::create).expectNext(value).verifyComplete();
+
+ redisTemplate.opsForValue().set(key, nextValue).as(StepVerifier::create).expectNext(true).verifyComplete();
+ redisTemplate.copy(key, targetKey, true).as(StepVerifier::create).expectNext(true).verifyComplete();
+ redisTemplate.opsForValue().get(targetKey).as(StepVerifier::create).expectNext(nextValue).verifyComplete();
+ }
+
@ParameterizedRedisTest // DATAREDIS-602
void exists() {
diff --git a/src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java
index 5a421a1d4..b6344e274 100644
--- a/src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java
+++ b/src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java
@@ -34,6 +34,8 @@ import org.springframework.data.redis.ObjectFactory;
import org.springframework.data.redis.Person;
import org.springframework.data.redis.SettingsUtils;
import org.springframework.data.redis.connection.DataType;
+import org.springframework.data.redis.connection.RedisClusterConnection;
+import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
@@ -393,16 +395,24 @@ public class RedisTemplateIntegrationTests {
@ParameterizedRedisTest
void testCopy() {
+
+ assumeThat(redisTemplate.execute((RedisCallback) it -> it))
+ .isNotInstanceOf(RedisClusterConnection.class);
+
K key1 = keyFactory.instance();
K key2 = keyFactory.instance();
V value1 = valueFactory.instance();
+ V value2 = valueFactory.instance();
redisTemplate.opsForValue().set(key1, value1);
- assertThat(redisTemplate.hasKey(key2)).isFalse();
- redisTemplate.copy(key1, key2);
- assertThat(redisTemplate.hasKey(key2)).isTrue();
+ redisTemplate.copy(key1, key2, false);
assertThat(redisTemplate.opsForValue().get(key2)).isEqualTo(value1);
+
+ redisTemplate.opsForValue().set(key1, value2);
+
+ redisTemplate.copy(key1, key2, true);
+ assertThat(redisTemplate.opsForValue().get(key2)).isEqualTo(value2);
}
@ParameterizedRedisTest // DATAREDIS-688