Skip to content

Commit 69e7137

Browse files
committed
Add support for COPY via RedisOperations and ReactiveRedisOperations
see spring-projects#2040
1 parent 485bcad commit 69e7137

15 files changed

+154
-0
lines changed

src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java

+19
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
* @author Tugdual Grall
7171
* @author Andrey Shlykov
7272
* @author dengliming
73+
* @author ihaohong
7374
*/
7475
public class DefaultStringRedisConnection implements StringRedisConnection, DecoratedRedisConnection {
7576

@@ -276,6 +277,15 @@ public Long del(byte[]... keys) {
276277
return convertAndReturn(delegate.del(keys), Converters.identityConverter());
277278
}
278279

280+
/*
281+
* (non-Javadoc)
282+
* @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
283+
*/
284+
@Override
285+
public Boolean copy(byte[] sourceKey, byte[] targetKey) {
286+
return convertAndReturn(delegate.copy(sourceKey, targetKey), Converters.identityConverter());
287+
}
288+
279289
/*
280290
* (non-Javadoc)
281291
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])
@@ -1899,6 +1909,15 @@ public Long del(String... keys) {
18991909
return del(serializeMulti(keys));
19001910
}
19011911

1912+
/*
1913+
* (non-Javadoc)
1914+
* @see org.springframework.data.redis.connection.StringRedisConnection#copy(java.lang.String[])
1915+
*/
1916+
@Override
1917+
public Boolean copy(String sourceKey, String targetKey) {
1918+
return copy(serialize(sourceKey), serialize(targetKey));
1919+
}
1920+
19021921
/*
19031922
* (non-Javadoc)
19041923
* @see org.springframework.data.redis.connection.StringRedisConnection#unlink(java.lang.String[])

src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java

+8
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
* @author Mark Paluch
5757
* @author Tugdual Grall
5858
* @author Andrey Shlykov
59+
* @author ihaohong
5960
* @since 2.0
6061
*/
6162
public interface DefaultedRedisConnection extends RedisConnection {
@@ -83,6 +84,13 @@ default Long del(byte[]... keys) {
8384
return keyCommands().del(keys);
8485
}
8586

87+
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
88+
@Override
89+
@Deprecated
90+
default Boolean copy(byte[] sourceKey, byte[] targetKey) {
91+
return keyCommands().copy(sourceKey, targetKey);
92+
}
93+
8694
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
8795
@Override
8896
@Deprecated

src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java

+12
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
* @author Costin Leau
3232
* @author Christoph Strobl
3333
* @author Mark Paluch
34+
* @author ihaohong
3435
*/
3536
public interface RedisKeyCommands {
3637

@@ -71,6 +72,17 @@ default Boolean exists(byte[] key) {
7172
@Nullable
7273
Long del(byte[]... keys);
7374

75+
/**
76+
* Copy given {@code sourceKey} to {@code targetKey}.
77+
*
78+
* @param sourceKey must not be {@literal null}.
79+
* @param targetKey must not be {@literal null}.
80+
* @return
81+
* @see <a href="https://redis.io/commands/copy">Redis Documentation: COPY</a>
82+
*/
83+
@Nullable
84+
Boolean copy(byte[] sourceKey, byte[] targetKey);
85+
7486
/**
7587
* Unlink the {@code keys} from the keyspace. Unlike with {@link #del(byte[]...)} the actual memory reclaiming here
7688
* happens asynchronously.

src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java

+13
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@
6464
* @author Tugdual Grall
6565
* @author Dengliming
6666
* @author Andrey Shlykov
67+
* @author ihaohong
68+
*
6769
* @see RedisCallback
6870
* @see RedisSerializer
6971
* @see StringRedisTemplate
@@ -132,6 +134,17 @@ interface StringTuple extends Tuple {
132134
*/
133135
Long del(String... keys);
134136

137+
/**
138+
* Copy given {@code sourceKey} to {@code targetKey}.
139+
*
140+
* @param sourceKey must not be {@literal null}.
141+
* @param targetKey must not be {@literal null}.
142+
* @return
143+
* @see <a href="https://redis.io/commands/copy">Redis Documentation: COPY</a>
144+
* @see RedisKeyCommands#copy(byte[], byte[])
145+
*/
146+
Boolean copy(String sourceKey, String targetKey);
147+
135148
/**
136149
* Unlink the {@code keys} from the keyspace. Unlike with {@link #del(String...)} the actual memory reclaiming here
137150
* happens asynchronously.

src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java

+13
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
/**
5454
* @author Christoph Strobl
5555
* @author Mark Paluch
56+
* @author ihaohong
5657
* @since 2.0
5758
*/
5859
class JedisClusterKeyCommands implements RedisKeyCommands {
@@ -87,6 +88,18 @@ public Long del(byte[]... keys) {
8788
.resultsAsList().size();
8889
}
8990

91+
/*
92+
* (non-Javadoc)
93+
* @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
94+
*/
95+
@Override
96+
public Boolean copy(byte[] sourceKey, byte[] targetKey) {
97+
Assert.notNull(sourceKey, "source key must not be null!");
98+
Assert.notNull(targetKey, "target key must not be null!");
99+
100+
return connection.getCluster().copy(sourceKey, targetKey, false);
101+
}
102+
90103
/*
91104
* (non-Javadoc)
92105
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])

src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java

+12
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
/**
4242
* @author Christoph Strobl
4343
* @author Mark Paluch
44+
* @author ihaohong
4445
* @since 2.0
4546
*/
4647
class JedisKeyCommands implements RedisKeyCommands {
@@ -90,6 +91,17 @@ public Long del(byte[]... keys) {
9091
return connection.invoke().just(BinaryJedis::del, MultiKeyPipelineBase::del, keys);
9192
}
9293

94+
/*
95+
* (non-Javadoc)
96+
* @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
97+
*/
98+
public Boolean copy(byte[] sourceKey, byte[] targetKey) {
99+
Assert.notNull(sourceKey, "source key must not be null!");
100+
Assert.notNull(targetKey, "target key must not be null!");
101+
102+
return connection.invoke().just(BinaryJedis::copy, MultiKeyPipelineBase::copy, sourceKey, targetKey, false);
103+
}
104+
93105
/*
94106
* (non-Javadoc)
95107
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])

src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java

+2
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
* @author Mark Paluch
8787
* @author Ninad Divadkar
8888
* @author Tamil Selvan
89+
* @author ihaohong
8990
*/
9091
public class LettuceConnection extends AbstractRedisConnection {
9192

@@ -1161,6 +1162,7 @@ static class TypeHints {
11611162
COMMAND_OUTPUT_TYPE_MAPPING.put(DECR, IntegerOutput.class);
11621163
COMMAND_OUTPUT_TYPE_MAPPING.put(DECRBY, IntegerOutput.class);
11631164
COMMAND_OUTPUT_TYPE_MAPPING.put(DEL, IntegerOutput.class);
1165+
COMMAND_OUTPUT_TYPE_MAPPING.put(COPY, IntegerOutput.class);
11641166
COMMAND_OUTPUT_TYPE_MAPPING.put(GETBIT, IntegerOutput.class);
11651167
COMMAND_OUTPUT_TYPE_MAPPING.put(HDEL, IntegerOutput.class);
11661168
COMMAND_OUTPUT_TYPE_MAPPING.put(HINCRBY, IntegerOutput.class);

src/main/java/org/springframework/data/redis/connection/lettuce/LettuceKeyCommands.java

+13
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
/**
4242
* @author Christoph Strobl
4343
* @author Mark Paluch
44+
* @author ihaohong
4445
* @since 2.0
4546
*/
4647
class LettuceKeyCommands implements RedisKeyCommands {
@@ -90,6 +91,18 @@ public Long del(byte[]... keys) {
9091
return connection.invoke().just(RedisKeyAsyncCommands::del, keys);
9192
}
9293

94+
/*
95+
* (non-Javadoc)
96+
* @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
97+
*/
98+
@Override
99+
public Boolean copy(byte[] sourceKey, byte[] targetKey) {
100+
Assert.notNull(sourceKey, "source key must not be null!");
101+
Assert.notNull(targetKey, "target key must not be null!");
102+
103+
return connection.invoke().just(RedisKeyAsyncCommands::copy, targetKey, sourceKey);
104+
}
105+
93106
/*
94107
* (non-Javadoc)
95108
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])

src/main/java/org/springframework/data/redis/core/RedisOperations.java

+12
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
* @author Christoph Strobl
4444
* @author Ninad Divadkar
4545
* @author Mark Paluch
46+
* @author ihaohong
4647
*/
4748
public interface RedisOperations<K, V> {
4849

@@ -199,6 +200,17 @@ <T> T execute(RedisScript<T> script, RedisSerializer<?> argsSerializer, RedisSer
199200
@Nullable
200201
Long delete(Collection<K> keys);
201202

203+
/**
204+
* Copy given {@code sourceKey} to {@code targetKey}.
205+
*
206+
* @param sourceKey must not be {@literal null}.
207+
* @param targetKey must not be {@literal null}.
208+
* @return
209+
* @see <a href="https://redis.io/commands/copy">Redis Documentation: COPY</a>
210+
*/
211+
@Nullable
212+
Boolean copy(K sourceKey, K targetKey);
213+
202214
/**
203215
* Unlink the {@code key} from the keyspace. Unlike with {@link #delete(Object)} the actual memory reclaiming here
204216
* happens asynchronously.

src/main/java/org/springframework/data/redis/core/RedisTemplate.java

+10
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@
8282
* @author Anqing Shao
8383
* @author Mark Paluch
8484
* @author Denis Zavedeev
85+
* @author ihaohong
86+
*
8587
* @param <K> the Redis key type against which the template works (usually a String)
8688
* @param <V> the Redis value type against which the template works
8789
* @see StringRedisTemplate
@@ -710,6 +712,14 @@ public Boolean delete(K key) {
710712
return result != null && result.intValue() == 1;
711713
}
712714

715+
@Override
716+
public Boolean copy(K source, K target) {
717+
byte[] sourceKey = rawKey(source);
718+
byte[] targetKey = rawKey(target);
719+
720+
return execute(connection -> connection.copy(sourceKey, targetKey), true);
721+
}
722+
713723
/*
714724
* (non-Javadoc)
715725
* @see org.springframework.data.redis.core.RedisOperations#delete(java.util.Collection)

src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java

+6
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,12 @@ public void testDel() {
137137
super.testDel();
138138
}
139139

140+
@Test
141+
public void testCopy() {
142+
doReturn(Collections.singletonList(Boolean.TRUE)).when(nativeConnection).closePipeline();
143+
super.testCopy();
144+
}
145+
140146
@Test
141147
public void testEchoBytes() {
142148
doReturn(Arrays.asList(new Object[] { barBytes })).when(nativeConnection).closePipeline();

src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java

+7
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,13 @@ public void testDel() {
141141
super.testDel();
142142
}
143143

144+
@Test
145+
public void testCopy() {
146+
doReturn(Collections.singletonList(Collections.singletonList(Boolean.TRUE))).when(nativeConnection).closePipeline();
147+
super.testCopy();
148+
}
149+
150+
144151
@Test
145152
public void testEchoBytes() {
146153
doReturn(Collections.singletonList(Arrays.asList(new Object[] { barBytes }))).when(nativeConnection)

src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java

+8
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
* @author Christoph Strobl
7171
* @author Ninad Divadkar
7272
* @author Mark Paluch
73+
* @author ihaohong
7374
*/
7475
@ExtendWith(MockitoExtension.class)
7576
@MockitoSettings(strictness = Strictness.LENIENT)
@@ -234,6 +235,13 @@ public void testDel() {
234235
verifyResults(Collections.singletonList(1L));
235236
}
236237

238+
@Test
239+
public void testCopy() {
240+
doReturn(Boolean.TRUE).when(nativeConnection).copy(fooBytes, barBytes);
241+
actual.add(connection.copy(foo, bar));
242+
verifyResults(Collections.singletonList(Boolean.TRUE));
243+
}
244+
237245
@Test
238246
public void testEchoBytes() {
239247
doReturn(barBytes).when(nativeConnection).echo(fooBytes);

src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java

+4
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ public Long del(byte[]... keys) {
187187
return delegate.del(keys);
188188
}
189189

190+
public Boolean copy(byte[] sourceKey, byte[] targetKey) {
191+
return delegate.copy(sourceKey, targetKey);
192+
}
193+
190194
public void close() throws DataAccessException {
191195
super.close();
192196
}

src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java

+15
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
* @author Anqing Shao
5959
* @author Duobiao Ou
6060
* @author Mark Paluch
61+
* @author ihaohong
6162
*/
6263
@MethodSource("testParams")
6364
public class RedisTemplateIntegrationTests<K, V> {
@@ -390,6 +391,20 @@ void testDelete() {
390391
assertThat(redisTemplate.hasKey(key1)).isFalse();
391392
}
392393

394+
@ParameterizedRedisTest
395+
void testCopy() {
396+
K key1 = keyFactory.instance();
397+
K key2 = keyFactory.instance();
398+
V value1 = valueFactory.instance();
399+
400+
redisTemplate.opsForValue().set(key1, value1);
401+
402+
assertThat(redisTemplate.hasKey(key2)).isFalse();
403+
redisTemplate.copy(key1, key2);
404+
assertThat(redisTemplate.hasKey(key2)).isTrue();
405+
assertThat(redisTemplate.opsForValue().get(key2)).isEqualTo(value1);
406+
}
407+
393408
@ParameterizedRedisTest // DATAREDIS-688
394409
void testDeleteMultiple() {
395410

0 commit comments

Comments
 (0)