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 spring-projects#2040 Original pull request: spring-projects#2059.
1 parent 1a4eef3 commit 7cd0669

21 files changed

+340
-103
lines changed

src/main/asciidoc/new-features.adoc

Lines changed: 1 addition & 1 deletion
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

Lines changed: 18 additions & 17 deletions
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

Lines changed: 7 additions & 7 deletions
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

Lines changed: 119 additions & 1 deletion
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

Lines changed: 13 additions & 11 deletions
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

Lines changed: 3 additions & 2 deletions
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

Lines changed: 13 additions & 12 deletions
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

Lines changed: 3 additions & 2 deletions
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

Lines changed: 15 additions & 11 deletions
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)