Add support for COPY.

Closes #2040
Original pull request: #2059.
This commit is contained in:
ihaohong
2021-05-04 18:55:20 +08:00
committed by Mark Paluch
parent ba767fa86d
commit 1a4eef347e
15 changed files with 154 additions and 0 deletions

View File

@@ -70,6 +70,7 @@ import org.springframework.util.ObjectUtils;
* @author Tugdual Grall * @author Tugdual Grall
* @author Andrey Shlykov * @author Andrey Shlykov
* @author dengliming * @author dengliming
* @author ihaohong
*/ */
public class DefaultStringRedisConnection implements StringRedisConnection, DecoratedRedisConnection { public class DefaultStringRedisConnection implements StringRedisConnection, DecoratedRedisConnection {
@@ -276,6 +277,15 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
return convertAndReturn(delegate.del(keys), Converters.identityConverter()); 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) * (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][]) * @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])
@@ -1917,6 +1927,15 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
return del(serializeMulti(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) * (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#unlink(java.lang.String[]) * @see org.springframework.data.redis.connection.StringRedisConnection#unlink(java.lang.String[])

View File

@@ -57,6 +57,7 @@ import org.springframework.lang.Nullable;
* @author Tugdual Grall * @author Tugdual Grall
* @author Andrey Shlykov * @author Andrey Shlykov
* @author dengliming * @author dengliming
* @author ihaohong
* @since 2.0 * @since 2.0
*/ */
public interface DefaultedRedisConnection extends RedisConnection { public interface DefaultedRedisConnection extends RedisConnection {
@@ -84,6 +85,13 @@ public interface DefaultedRedisConnection extends RedisConnection {
return keyCommands().del(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()}. */ /** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
@Override @Override
@Deprecated @Deprecated

View File

@@ -31,6 +31,7 @@ import org.springframework.util.Assert;
* @author Costin Leau * @author Costin Leau
* @author Christoph Strobl * @author Christoph Strobl
* @author Mark Paluch * @author Mark Paluch
* @author ihaohong
*/ */
public interface RedisKeyCommands { public interface RedisKeyCommands {
@@ -71,6 +72,17 @@ public interface RedisKeyCommands {
@Nullable @Nullable
Long del(byte[]... keys); 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 <a href="https://redis.io/commands/copy">Redis Documentation: COPY</a>
*/
@Nullable
Boolean copy(byte[] sourceKey, byte[] targetKey);
/** /**
* Unlink the {@code keys} from the keyspace. Unlike with {@link #del(byte[]...)} the actual memory reclaiming here * Unlink the {@code keys} from the keyspace. Unlike with {@link #del(byte[]...)} the actual memory reclaiming here
* happens asynchronously. * happens asynchronously.

View File

@@ -64,6 +64,8 @@ import org.springframework.util.CollectionUtils;
* @author Tugdual Grall * @author Tugdual Grall
* @author Dengliming * @author Dengliming
* @author Andrey Shlykov * @author Andrey Shlykov
* @author ihaohong
*
* @see RedisCallback * @see RedisCallback
* @see RedisSerializer * @see RedisSerializer
* @see StringRedisTemplate * @see StringRedisTemplate
@@ -132,6 +134,17 @@ public interface StringRedisConnection extends RedisConnection {
*/ */
Long del(String... keys); 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 <a href="https://redis.io/commands/copy">Redis Documentation: COPY</a>
* @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 * Unlink the {@code keys} from the keyspace. Unlike with {@link #del(String...)} the actual memory reclaiming here
* happens asynchronously. * happens asynchronously.

View File

@@ -53,6 +53,7 @@ import org.springframework.util.ObjectUtils;
/** /**
* @author Christoph Strobl * @author Christoph Strobl
* @author Mark Paluch * @author Mark Paluch
* @author ihaohong
* @since 2.0 * @since 2.0
*/ */
class JedisClusterKeyCommands implements RedisKeyCommands { class JedisClusterKeyCommands implements RedisKeyCommands {
@@ -87,6 +88,18 @@ class JedisClusterKeyCommands implements RedisKeyCommands {
.resultsAsList().size(); .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) * (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][]) * @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])

View File

@@ -41,6 +41,7 @@ import org.springframework.util.Assert;
/** /**
* @author Christoph Strobl * @author Christoph Strobl
* @author Mark Paluch * @author Mark Paluch
* @author ihaohong
* @since 2.0 * @since 2.0
*/ */
class JedisKeyCommands implements RedisKeyCommands { class JedisKeyCommands implements RedisKeyCommands {
@@ -90,6 +91,17 @@ class JedisKeyCommands implements RedisKeyCommands {
return connection.invoke().just(BinaryJedis::del, MultiKeyPipelineBase::del, 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) * (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][]) * @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])

View File

@@ -86,6 +86,7 @@ import org.springframework.util.ObjectUtils;
* @author Mark Paluch * @author Mark Paluch
* @author Ninad Divadkar * @author Ninad Divadkar
* @author Tamil Selvan * @author Tamil Selvan
* @author ihaohong
*/ */
public class LettuceConnection extends AbstractRedisConnection { public class LettuceConnection extends AbstractRedisConnection {
@@ -1161,6 +1162,7 @@ public class LettuceConnection extends AbstractRedisConnection {
COMMAND_OUTPUT_TYPE_MAPPING.put(DECR, IntegerOutput.class); COMMAND_OUTPUT_TYPE_MAPPING.put(DECR, IntegerOutput.class);
COMMAND_OUTPUT_TYPE_MAPPING.put(DECRBY, IntegerOutput.class); COMMAND_OUTPUT_TYPE_MAPPING.put(DECRBY, IntegerOutput.class);
COMMAND_OUTPUT_TYPE_MAPPING.put(DEL, 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(GETBIT, IntegerOutput.class);
COMMAND_OUTPUT_TYPE_MAPPING.put(HDEL, IntegerOutput.class); COMMAND_OUTPUT_TYPE_MAPPING.put(HDEL, IntegerOutput.class);
COMMAND_OUTPUT_TYPE_MAPPING.put(HINCRBY, IntegerOutput.class); COMMAND_OUTPUT_TYPE_MAPPING.put(HINCRBY, IntegerOutput.class);

View File

@@ -41,6 +41,7 @@ import org.springframework.util.Assert;
/** /**
* @author Christoph Strobl * @author Christoph Strobl
* @author Mark Paluch * @author Mark Paluch
* @author ihaohong
* @since 2.0 * @since 2.0
*/ */
class LettuceKeyCommands implements RedisKeyCommands { class LettuceKeyCommands implements RedisKeyCommands {
@@ -90,6 +91,18 @@ class LettuceKeyCommands implements RedisKeyCommands {
return connection.invoke().just(RedisKeyAsyncCommands::del, 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) * (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][]) * @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])

View File

@@ -43,6 +43,7 @@ import org.springframework.util.Assert;
* @author Christoph Strobl * @author Christoph Strobl
* @author Ninad Divadkar * @author Ninad Divadkar
* @author Mark Paluch * @author Mark Paluch
* @author ihaohong
*/ */
public interface RedisOperations<K, V> { public interface RedisOperations<K, V> {
@@ -199,6 +200,17 @@ public interface RedisOperations<K, V> {
@Nullable @Nullable
Long delete(Collection<K> keys); Long delete(Collection<K> keys);
/**
* Copy given {@code sourceKey} to {@code targetKey}.
*
* @param sourceKey must not be {@literal null}.
* @param targetKey must not be {@literal null}.
* @return
* @see <a href="https://redis.io/commands/copy">Redis Documentation: COPY</a>
*/
@Nullable
Boolean copy(K sourceKey, K targetKey);
/** /**
* Unlink the {@code key} from the keyspace. Unlike with {@link #delete(Object)} the actual memory reclaiming here * Unlink the {@code key} from the keyspace. Unlike with {@link #delete(Object)} the actual memory reclaiming here
* happens asynchronously. * happens asynchronously.

View File

@@ -82,6 +82,8 @@ import org.springframework.util.CollectionUtils;
* @author Anqing Shao * @author Anqing Shao
* @author Mark Paluch * @author Mark Paluch
* @author Denis Zavedeev * @author Denis Zavedeev
* @author ihaohong
*
* @param <K> the Redis key type against which the template works (usually a String) * @param <K> the Redis key type against which the template works (usually a String)
* @param <V> the Redis value type against which the template works * @param <V> the Redis value type against which the template works
* @see StringRedisTemplate * @see StringRedisTemplate
@@ -710,6 +712,14 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
return result != null && result.intValue() == 1; 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) * (non-Javadoc)
* @see org.springframework.data.redis.core.RedisOperations#delete(java.util.Collection) * @see org.springframework.data.redis.core.RedisOperations#delete(java.util.Collection)

View File

@@ -138,6 +138,12 @@ public class DefaultStringRedisConnectionPipelineTests extends DefaultStringRedi
super.testDel(); super.testDel();
} }
@Test
public void testCopy() {
doReturn(Collections.singletonList(Boolean.TRUE)).when(nativeConnection).closePipeline();
super.testCopy();
}
@Test @Test
public void testEchoBytes() { public void testEchoBytes() {
doReturn(Arrays.asList(new Object[] { barBytes })).when(nativeConnection).closePipeline(); doReturn(Arrays.asList(new Object[] { barBytes })).when(nativeConnection).closePipeline();

View File

@@ -142,6 +142,13 @@ public class DefaultStringRedisConnectionPipelineTxTests extends DefaultStringRe
super.testDel(); super.testDel();
} }
@Test
public void testCopy() {
doReturn(Collections.singletonList(Collections.singletonList(Boolean.TRUE))).when(nativeConnection).closePipeline();
super.testCopy();
}
@Test @Test
public void testEchoBytes() { public void testEchoBytes() {
doReturn(Collections.singletonList(Arrays.asList(new Object[] { barBytes }))).when(nativeConnection) doReturn(Collections.singletonList(Arrays.asList(new Object[] { barBytes }))).when(nativeConnection)

View File

@@ -71,6 +71,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
* @author Ninad Divadkar * @author Ninad Divadkar
* @author Mark Paluch * @author Mark Paluch
* @author dengliming * @author dengliming
* @author ihaohong
*/ */
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT) @MockitoSettings(strictness = Strictness.LENIENT)
@@ -235,6 +236,13 @@ public class DefaultStringRedisConnectionTests {
verifyResults(Collections.singletonList(1L)); 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 @Test
public void testEchoBytes() { public void testEchoBytes() {
doReturn(barBytes).when(nativeConnection).echo(fooBytes); doReturn(barBytes).when(nativeConnection).echo(fooBytes);

View File

@@ -187,6 +187,10 @@ class RedisConnectionUnitTests {
return delegate.del(keys); return delegate.del(keys);
} }
public Boolean copy(byte[] sourceKey, byte[] targetKey) {
return delegate.copy(sourceKey, targetKey);
}
public void close() throws DataAccessException { public void close() throws DataAccessException {
super.close(); super.close();
} }

View File

@@ -58,6 +58,7 @@ import org.springframework.data.redis.test.util.CollectionAwareComparator;
* @author Anqing Shao * @author Anqing Shao
* @author Duobiao Ou * @author Duobiao Ou
* @author Mark Paluch * @author Mark Paluch
* @author ihaohong
*/ */
@MethodSource("testParams") @MethodSource("testParams")
public class RedisTemplateIntegrationTests<K, V> { public class RedisTemplateIntegrationTests<K, V> {
@@ -390,6 +391,20 @@ public class RedisTemplateIntegrationTests<K, V> {
assertThat(redisTemplate.hasKey(key1)).isFalse(); 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 @ParameterizedRedisTest // DATAREDIS-688
void testDeleteMultiple() { void testDeleteMultiple() {