Skip to content

Commit 7cd0669

Browse files
committed
Polishing.
Extend copy(…) command with replace argument. Add support for reactive copy(…). Fix connection unit tests. Reorder methods. Add since tags. See #2040 Original pull request: #2059.
1 parent 1a4eef3 commit 7cd0669

21 files changed

+340
-103
lines changed

src/main/asciidoc/new-features.adoc

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ This section briefly covers items that are new and noteworthy in the latest rele
77
== New in Spring Data Redis 2.6
88

99
* 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.
10-
* Support Redis 6.2 commands (`LPOP`/`RPOP` with `count`).
10+
* Support Redis 6.2 commands (`LPOP`/`RPOP` with `count`, `COPY`).
1111

1212
[[new-in-2.5.0]]
1313
== New in Spring Data Redis 2.5

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

+18-17
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,15 @@ public void close() throws RedisSystemException {
241241
delegate.close();
242242
}
243243

244+
/*
245+
* (non-Javadoc)
246+
* @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[], boolean)
247+
*/
248+
@Override
249+
public Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace) {
250+
return convertAndReturn(delegate.copy(sourceKey, targetKey, replace), Converters.identityConverter());
251+
}
252+
244253
/*
245254
* (non-Javadoc)
246255
* @see org.springframework.data.redis.connection.RedisServerCommands#dbSize()
@@ -268,6 +277,7 @@ public Long decrBy(byte[] key, long value) {
268277
return convertAndReturn(delegate.decrBy(key, value), Converters.identityConverter());
269278
}
270279

280+
271281
/*
272282
* (non-Javadoc)
273283
* @see org.springframework.data.redis.connection.RedisKeyCommands#del(byte[][])
@@ -277,15 +287,6 @@ public Long del(byte[]... keys) {
277287
return convertAndReturn(delegate.del(keys), Converters.identityConverter());
278288
}
279289

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-
289290
/*
290291
* (non-Javadoc)
291292
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])
@@ -1900,6 +1901,14 @@ public String bRPopLPush(int timeout, String srcKey, String dstKey) {
19001901
return convertAndReturn(delegate.bRPopLPush(timeout, serialize(srcKey), serialize(dstKey)), bytesToString);
19011902
}
19021903

1904+
/*
1905+
* (non-Javadoc)
1906+
* @see org.springframework.data.redis.connection.StringRedisConnection#copy(java.lang.String, java.lang.String, boolean)
1907+
*/
1908+
@Override
1909+
public Boolean copy(String sourceKey, String targetKey, boolean replace) {
1910+
return copy(serialize(sourceKey), serialize(targetKey), replace);
1911+
}
19031912
/*
19041913
* (non-Javadoc)
19051914
* @see org.springframework.data.redis.connection.StringRedisConnection#decr(java.lang.String)
@@ -1927,14 +1936,6 @@ public Long del(String... keys) {
19271936
return del(serializeMulti(keys));
19281937
}
19291938

1930-
/*
1931-
* (non-Javadoc)
1932-
* @see org.springframework.data.redis.connection.StringRedisConnection#copy(java.lang.String[])
1933-
*/
1934-
@Override
1935-
public Boolean copy(String sourceKey, String targetKey) {
1936-
return copy(serialize(sourceKey), serialize(targetKey));
1937-
}
19381939

19391940
/*
19401941
* (non-Javadoc)

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

+7-7
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ public interface DefaultedRedisConnection extends RedisConnection {
6464

6565
// KEY COMMANDS
6666

67+
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
68+
@Override
69+
@Deprecated
70+
default Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace) {
71+
return keyCommands().copy(sourceKey, targetKey, replace);
72+
}
73+
6774
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
6875
@Override
6976
@Deprecated
@@ -85,13 +92,6 @@ default Long del(byte[]... keys) {
8592
return keyCommands().del(keys);
8693
}
8794

88-
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
89-
@Override
90-
@Deprecated
91-
default Boolean copy(byte[] sourceKey, byte[] targetKey) {
92-
return keyCommands().copy(sourceKey, targetKey);
93-
}
94-
9595
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
9696
@Override
9797
@Deprecated

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

+119-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,124 @@
4343
*/
4444
public interface ReactiveKeyCommands {
4545

46+
/**
47+
* {@code MOVE} command parameters.
48+
*
49+
* @author Mark Paluch
50+
* @see <a href="https://redis.io/commands/move">Redis Documentation: MOVE</a>
51+
*/
52+
class CopyCommand extends KeyCommand {
53+
54+
private final @Nullable ByteBuffer target;
55+
private final boolean replace;
56+
private final @Nullable Integer database;
57+
58+
public CopyCommand(ByteBuffer key, @Nullable ByteBuffer target, boolean replace, @Nullable Integer database) {
59+
super(key);
60+
this.target = target;
61+
this.replace = replace;
62+
this.database = database;
63+
}
64+
65+
/**
66+
* Creates a new {@link CopyCommand} given a {@link ByteBuffer key}.
67+
*
68+
* @param key must not be {@literal null}.
69+
* @return a new {@link CopyCommand} for {@link ByteBuffer key}.
70+
*/
71+
public static CopyCommand key(ByteBuffer key) {
72+
73+
Assert.notNull(key, "Key must not be null!");
74+
75+
return new CopyCommand(key, null, false, null);
76+
}
77+
78+
/**
79+
* Applies the {@link ByteBuffer targetKey}. Constructs a new command instance with all previously configured
80+
* properties.
81+
*
82+
* @param targetKey must not be {@literal null}.
83+
* @return a new {@link CopyCommand} with {@literal database} applied.
84+
*/
85+
public CopyCommand to(ByteBuffer targetKey) {
86+
87+
Assert.notNull(targetKey, "Key must not be null!");
88+
89+
return new CopyCommand(getKey(), targetKey, isReplace(), database);
90+
}
91+
92+
/**
93+
* Applies {@code replace}. Constructs a new command instance with all previously configured properties.
94+
*
95+
* @param key must not be {@literal null}.
96+
* @return a new {@link CopyCommand} with {@literal replace} applied.
97+
*/
98+
public CopyCommand replace(boolean replace) {
99+
return new CopyCommand(getKey(), this.target, replace, database);
100+
}
101+
102+
/**
103+
* Applies the {@literal database} index. Constructs a new command instance with all previously configured
104+
* properties.
105+
*
106+
* @param database
107+
* @return a new {@link CopyCommand} with {@literal database} applied.
108+
*/
109+
public CopyCommand database(int database) {
110+
return new CopyCommand(getKey(), this.target, isReplace(), database);
111+
}
112+
113+
/**
114+
* @return can be {@literal null}.
115+
*/
116+
@Nullable
117+
public ByteBuffer getTarget() {
118+
return target;
119+
}
120+
121+
public boolean isReplace() {
122+
return replace;
123+
}
124+
125+
/**
126+
* @return can be {@literal null}.
127+
*/
128+
@Nullable
129+
public Integer getDatabase() {
130+
return database;
131+
}
132+
133+
}
134+
135+
/**
136+
* Copy given {@code key} to a target {@code key}.
137+
*
138+
* @param sourceKey must not be {@literal null}.
139+
* @param targetKey must not be {@literal null}.
140+
* @param replace whether to replace existing keys.
141+
* @return
142+
* @see <a href="https://redis.io/commands/copy">Redis Documentation: COPY</a>
143+
* @since 2.6
144+
*/
145+
default Mono<Boolean> copy(ByteBuffer sourceKey, ByteBuffer targetKey, boolean replace) {
146+
147+
Assert.notNull(sourceKey, "Source key must not be null!");
148+
Assert.notNull(targetKey, "Targetk ey must not be null!");
149+
150+
return copy(Mono.just(CopyCommand.key(sourceKey).to(targetKey).replace(replace))).next()
151+
.map(BooleanResponse::getOutput);
152+
}
153+
154+
/**
155+
* Copy keys one-by-one.
156+
*
157+
* @param commands must not be {@literal null}.
158+
* @return {@link Flux} of {@link BooleanResponse} holding the {@literal key} to move along with the copy result.
159+
* @see <a href="https://redis.io/commands/copy">Redis Documentation: COPY</a>
160+
* @since 2.6
161+
*/
162+
Flux<BooleanResponse<CopyCommand>> copy(Publisher<CopyCommand> commands);
163+
46164
/**
47165
* Determine if given {@literal key} exists.
48166
*
@@ -670,7 +788,7 @@ private MoveCommand(ByteBuffer key, @Nullable Integer database) {
670788
* Creates a new {@link MoveCommand} given a {@link ByteBuffer key}.
671789
*
672790
* @param key must not be {@literal null}.
673-
* @return a new {@link ExpireCommand} for {@link ByteBuffer key}.
791+
* @return a new {@link MoveCommand} for {@link ByteBuffer key}.
674792
*/
675793
public static MoveCommand key(ByteBuffer key) {
676794

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

+13-11
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,19 @@
3535
*/
3636
public interface RedisKeyCommands {
3737

38+
/**
39+
* Copy given {@code sourceKey} to {@code targetKey}.
40+
*
41+
* @param sourceKey must not be {@literal null}.
42+
* @param targetKey must not be {@literal null}.
43+
* @param replace whether to replace existing keys.
44+
* @return {@literal null} when used in pipeline / transaction.
45+
* @see <a href="https://redis.io/commands/copy">Redis Documentation: COPY</a>
46+
* @since 2.6
47+
*/
48+
@Nullable
49+
Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace);
50+
3851
/**
3952
* Determine if given {@code key} exists.
4053
*
@@ -72,17 +85,6 @@ default Boolean exists(byte[] key) {
7285
@Nullable
7386
Long del(byte[]... keys);
7487

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-
8688
/**
8789
* Unlink the {@code keys} from the keyspace. Unlike with {@link #del(byte[]...)} the actual memory reclaiming here
8890
* happens asynchronously.

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,12 @@ interface StringTuple extends Tuple {
139139
*
140140
* @param sourceKey must not be {@literal null}.
141141
* @param targetKey must not be {@literal null}.
142-
* @return
142+
* @param replace whether to replace existing keys.
143+
* @return {@literal null} when used in pipeline / transaction.
143144
* @see <a href="https://redis.io/commands/copy">Redis Documentation: COPY</a>
144145
* @see RedisKeyCommands#copy(byte[], byte[])
145146
*/
146-
Boolean copy(String sourceKey, String targetKey);
147+
Boolean copy(String sourceKey, String targetKey, boolean replace);
147148

148149
/**
149150
* Unlink the {@code keys} from the keyspace. Unlike with {@link #del(String...)} the actual memory reclaiming here

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

+13-12
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,19 @@ class JedisClusterKeyCommands implements RedisKeyCommands {
6464
this.connection = connection;
6565
}
6666

67+
/*
68+
* (non-Javadoc)
69+
* @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
70+
*/
71+
@Override
72+
public Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace) {
73+
74+
Assert.notNull(sourceKey, "source key must not be null!");
75+
Assert.notNull(targetKey, "target key must not be null!");
76+
77+
return connection.getCluster().copy(sourceKey, targetKey, replace);
78+
}
79+
6780
/*
6881
* (non-Javadoc)
6982
* @see org.springframework.data.redis.connection.RedisKeyCommands#del(byte[][])
@@ -88,18 +101,6 @@ public Long del(byte[]... keys) {
88101
.resultsAsList().size();
89102
}
90103

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-
103104
/*
104105
* (non-Javadoc)
105106
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,12 @@ public Long del(byte[]... keys) {
9595
* (non-Javadoc)
9696
* @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
9797
*/
98-
public Boolean copy(byte[] sourceKey, byte[] targetKey) {
98+
public Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace) {
99+
99100
Assert.notNull(sourceKey, "source key must not be null!");
100101
Assert.notNull(targetKey, "target key must not be null!");
101102

102-
return connection.invoke().just(BinaryJedis::copy, MultiKeyPipelineBase::copy, sourceKey, targetKey, false);
103+
return connection.invoke().just(BinaryJedis::copy, MultiKeyPipelineBase::copy, sourceKey, targetKey, replace);
103104
}
104105

105106
/*

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

+15-11
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.data.redis.connection.lettuce;
1717

18+
import io.lettuce.core.CopyArgs;
1819
import io.lettuce.core.KeyScanCursor;
1920
import io.lettuce.core.RestoreArgs;
2021
import io.lettuce.core.ScanArgs;
@@ -52,6 +53,20 @@ class LettuceKeyCommands implements RedisKeyCommands {
5253
this.connection = connection;
5354
}
5455

56+
/*
57+
* (non-Javadoc)
58+
* @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
59+
*/
60+
@Override
61+
public Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace) {
62+
63+
Assert.notNull(sourceKey, "source key must not be null!");
64+
Assert.notNull(targetKey, "target key must not be null!");
65+
66+
return connection.invoke().just(RedisKeyAsyncCommands::copy, sourceKey, targetKey,
67+
CopyArgs.Builder.replace(replace));
68+
}
69+
5570
/*
5671
* (non-Javadoc)
5772
* @see org.springframework.data.redis.connection.RedisKeyCommands#exists(byte[])
@@ -91,17 +106,6 @@ public Long del(byte[]... keys) {
91106
return connection.invoke().just(RedisKeyAsyncCommands::del, keys);
92107
}
93108

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-
}
105109

106110
/*
107111
* (non-Javadoc)

0 commit comments

Comments
 (0)