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 89820ee1e8..6da75c6c76 100644
--- a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java
@@ -70,6 +70,7 @@
* @author Tugdual Grall
* @author Andrey Shlykov
* @author dengliming
+ * @author ihaohong
*/
public class DefaultStringRedisConnection implements StringRedisConnection, DecoratedRedisConnection {
@@ -276,6 +277,15 @@ public Long del(byte[]... keys) {
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[][])
@@ -1899,6 +1909,15 @@ public Long del(String... keys) {
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)
* @see org.springframework.data.redis.connection.StringRedisConnection#unlink(java.lang.String[])
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 6178634997..d70a785da7 100644
--- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
@@ -56,6 +56,7 @@
* @author Mark Paluch
* @author Tugdual Grall
* @author Andrey Shlykov
+ * @author ihaohong
* @since 2.0
*/
public interface DefaultedRedisConnection extends RedisConnection {
@@ -83,6 +84,13 @@ default Long del(byte[]... keys) {
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/RedisKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java
index 9716a4a30a..908c6ed60a 100644
--- a/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java
@@ -31,6 +31,7 @@
* @author Costin Leau
* @author Christoph Strobl
* @author Mark Paluch
+ * @author ihaohong
*/
public interface RedisKeyCommands {
@@ -71,6 +72,17 @@ default Boolean exists(byte[] key) {
@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 c3f2ad9fbe..c3d87498ad 100644
--- a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java
@@ -64,6 +64,8 @@
* @author Tugdual Grall
* @author Dengliming
* @author Andrey Shlykov
+ * @author ihaohong
+ *
* @see RedisCallback
* @see RedisSerializer
* @see StringRedisTemplate
@@ -132,6 +134,17 @@ interface StringTuple extends Tuple {
*/
Long del(String... 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
+ * @see RedisKeyCommands#copy(byte[], byte[])
+ */
+ Boolean copy(String sourceKey, String targetKey);
+
/**
* Unlink the {@code keys} from the keyspace. Unlike with {@link #del(String...)} the actual memory reclaiming here
* happens asynchronously.
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 627ab54e7b..a789371873 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
@@ -53,6 +53,7 @@
/**
* @author Christoph Strobl
* @author Mark Paluch
+ * @author ihaohong
* @since 2.0
*/
class JedisClusterKeyCommands implements RedisKeyCommands {
@@ -87,6 +88,18 @@ public Long del(byte[]... keys) {
.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 969727ce94..81cfb1d5b6 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
@@ -41,6 +41,7 @@
/**
* @author Christoph Strobl
* @author Mark Paluch
+ * @author ihaohong
* @since 2.0
*/
class JedisKeyCommands implements RedisKeyCommands {
@@ -90,6 +91,17 @@ public Long del(byte[]... keys) {
return connection.invoke().just(BinaryJedis::del, MultiKeyPipelineBase::del, keys);
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
+ */
+ 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(BinaryJedis::copy, MultiKeyPipelineBase::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/lettuce/LettuceConnection.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java
index 708344cc28..edac71ad27 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java
@@ -86,6 +86,7 @@
* @author Mark Paluch
* @author Ninad Divadkar
* @author Tamil Selvan
+ * @author ihaohong
*/
public class LettuceConnection extends AbstractRedisConnection {
@@ -1161,6 +1162,7 @@ static class TypeHints {
COMMAND_OUTPUT_TYPE_MAPPING.put(DECR, IntegerOutput.class);
COMMAND_OUTPUT_TYPE_MAPPING.put(DECRBY, IntegerOutput.class);
COMMAND_OUTPUT_TYPE_MAPPING.put(DEL, IntegerOutput.class);
+ COMMAND_OUTPUT_TYPE_MAPPING.put(COPY, IntegerOutput.class);
COMMAND_OUTPUT_TYPE_MAPPING.put(GETBIT, IntegerOutput.class);
COMMAND_OUTPUT_TYPE_MAPPING.put(HDEL, IntegerOutput.class);
COMMAND_OUTPUT_TYPE_MAPPING.put(HINCRBY, IntegerOutput.class);
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 f69cb7b832..3ff5da5f05 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
@@ -41,6 +41,7 @@
/**
* @author Christoph Strobl
* @author Mark Paluch
+ * @author ihaohong
* @since 2.0
*/
class LettuceKeyCommands implements RedisKeyCommands {
@@ -90,6 +91,18 @@ public Long del(byte[]... keys) {
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)
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])
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 14fdfbba64..5ac17f9e04 100644
--- a/src/main/java/org/springframework/data/redis/core/RedisOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/RedisOperations.java
@@ -43,6 +43,7 @@
* @author Christoph Strobl
* @author Ninad Divadkar
* @author Mark Paluch
+ * @author ihaohong
*/
public interface RedisOperations {
@@ -199,6 +200,17 @@ T execute(RedisScript script, RedisSerializer> argsSerializer, RedisSer
@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 be39541513..6448a00fc9 100644
--- a/src/main/java/org/springframework/data/redis/core/RedisTemplate.java
+++ b/src/main/java/org/springframework/data/redis/core/RedisTemplate.java
@@ -82,6 +82,8 @@
* @author Anqing Shao
* @author Mark Paluch
* @author Denis Zavedeev
+ * @author ihaohong
+ *
* @param the Redis key type against which the template works (usually a String)
* @param the Redis value type against which the template works
* @see StringRedisTemplate
@@ -710,6 +712,14 @@ public Boolean delete(K key) {
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/DefaultStringRedisConnectionPipelineTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java
index 84e501c269..0a0348aa6b 100644
--- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java
+++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java
@@ -137,6 +137,12 @@ public void testDel() {
super.testDel();
}
+ @Test
+ public void testCopy() {
+ doReturn(Collections.singletonList(Boolean.TRUE)).when(nativeConnection).closePipeline();
+ super.testCopy();
+ }
+
@Test
public void testEchoBytes() {
doReturn(Arrays.asList(new Object[] { barBytes })).when(nativeConnection).closePipeline();
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 882d11f595..86f302d2d4 100644
--- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java
+++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java
@@ -141,6 +141,13 @@ public void testDel() {
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 0bce55b824..b09c19e666 100644
--- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java
+++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java
@@ -70,6 +70,7 @@
* @author Christoph Strobl
* @author Ninad Divadkar
* @author Mark Paluch
+ * @author ihaohong
*/
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
@@ -234,6 +235,13 @@ public void testDel() {
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/RedisConnectionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java
index 4f730ced5a..318549d098 100644
--- a/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java
+++ b/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java
@@ -187,6 +187,10 @@ public Long del(byte[]... keys) {
return delegate.del(keys);
}
+ public Boolean copy(byte[] sourceKey, byte[] targetKey) {
+ return delegate.copy(sourceKey, targetKey);
+ }
+
public void close() throws DataAccessException {
super.close();
}
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 55e3146ad6..5a421a1d4d 100644
--- a/src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java
+++ b/src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java
@@ -58,6 +58,7 @@
* @author Anqing Shao
* @author Duobiao Ou
* @author Mark Paluch
+ * @author ihaohong
*/
@MethodSource("testParams")
public class RedisTemplateIntegrationTests {
@@ -390,6 +391,20 @@ void testDelete() {
assertThat(redisTemplate.hasKey(key1)).isFalse();
}
+ @ParameterizedRedisTest
+ void testCopy() {
+ K key1 = keyFactory.instance();
+ K key2 = keyFactory.instance();
+ V value1 = valueFactory.instance();
+
+ redisTemplate.opsForValue().set(key1, value1);
+
+ assertThat(redisTemplate.hasKey(key2)).isFalse();
+ redisTemplate.copy(key1, key2);
+ assertThat(redisTemplate.hasKey(key2)).isTrue();
+ assertThat(redisTemplate.opsForValue().get(key2)).isEqualTo(value1);
+ }
+
@ParameterizedRedisTest // DATAREDIS-688
void testDeleteMultiple() {