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 57b86e8af..56da1a9fd 100644
--- a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java
@@ -993,15 +993,6 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
return convertAndReturn(delegate.set(key, value, expiration, option), identityConverter);
}
- /*
- * (non-Javadoc)
- * @see org.springframework.data.redis.connection.RedisStringCommands#set(byte[], byte[], org.springframework.data.redis.core.types.Expiration, org.springframework.data.redis.connection.RedisStringCommands.SetOptions, boolean)
- */
- @Override
- public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption option, boolean keepTtl) {
- return convertAndReturn(delegate.set(key, value, expiration, option, keepTtl), identityConverter);
- }
-
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisStringCommands#setBit(byte[], long, boolean)
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 0cf9bbbf0..4b66f1fdf 100644
--- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
@@ -294,13 +294,6 @@ public interface DefaultedRedisConnection extends RedisConnection {
return stringCommands().set(key, value, expiration, option);
}
- /** @deprecated in favor of {@link RedisConnection#stringCommands()}}. */
- @Override
- @Deprecated
- default Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption option, boolean keepTtl) {
- return stringCommands().set(key, value, expiration, option, keepTtl);
- }
-
/** @deprecated in favor of {@link RedisConnection#stringCommands()}}. */
@Override
@Deprecated
diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java
index 0ceea07cb..093f18b85 100644
--- a/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java
@@ -169,7 +169,8 @@ public interface ReactiveStringCommands {
*
* @param key must not be {@literal null}.
* @param value must not be {@literal null}.
- * @param expiration must not be {@literal null}.
+ * @param expiration must not be {@literal null}. Use {@link Expiration#persistent()} for no expiration time or
+ * {@link Expiration#keepTtl()} to keep the existing.
* @param option must not be {@literal null}.
* @return
* @see Redis Documentation: SET
diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java
index 8e888c343..bb639e52f 100644
--- a/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java
@@ -83,7 +83,8 @@ public interface RedisStringCommands {
*
* @param key must not be {@literal null}.
* @param value must not be {@literal null}.
- * @param expiration must not be {@literal null}. Use {@link Expiration#persistent()} to not set any ttl.
+ * @param expiration must not be {@literal null}. Use {@link Expiration#persistent()} to not set any ttl or
+ * {@link Expiration#keepTtl()} to keep the existing expiration.
* @param option must not be {@literal null}. Use {@link SetOption#upsert()} to add non existing.
* @return {@literal null} when used in pipeline / transaction.
* @since 1.7
@@ -92,22 +93,6 @@ public interface RedisStringCommands {
@Nullable
Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption option);
- /**
- * Set {@code value} for {@code key} applying timeouts from {@code expiration} if set and inserting/updating values
- * depending on {@code option}.
- *
- * @param key must not be {@literal null}.
- * @param value must not be {@literal null}.
- * @param expiration must not be {@literal null}. Use {@link Expiration#persistent()} to not set any ttl.
- * @param option must not be {@literal null}. Use {@link SetOption#upsert()} to add non existing.
- * @param keepTtl set the value and retain the existing TTL.
- * @return {@literal null} when used in pipeline / transaction.
- * @since 2.4
- * @see Redis Documentation: SET
- */
- @Nullable
- Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption option, boolean keepTtl);
-
/**
* Set {@code value} for {@code key}, only if {@code key} does not exist.
*
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 7730abbeb..7a016c265 100644
--- a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java
@@ -420,7 +420,8 @@ public interface StringRedisConnection extends RedisConnection {
*
* @param key must not be {@literal null}.
* @param value must not be {@literal null}.
- * @param expiration can be {@literal null}. Defaulted to {@link Expiration#persistent()}.
+ * @param expiration can be {@literal null}. Defaulted to {@link Expiration#persistent()}. Use
+ * {@link Expiration#keepTtl()} to keep the existing expiration.
* @param option can be {@literal null}. Defaulted to {@link SetOption#UPSERT}.
* @since 1.7
* @see Redis Documentation: SET
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterStringCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterStringCommands.java
index 99d67bc48..5f57de963 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterStringCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterStringCommands.java
@@ -127,20 +127,11 @@ class JedisClusterStringCommands implements RedisStringCommands {
*/
@Override
public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption option) {
- return set(key, value, expiration, option, false);
- }
- /*
- * (non-Javadoc)
- * @see org.springframework.data.redis.connection.RedisStringCommands#set(byte[], byte[], org.springframework.data.redis.core.types.Expiration, org.springframework.data.redis.connection.RedisStringCommands.SetOptions, boolean)
- */
- @Override
- public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption option, boolean keepTtl) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(value, "Value must not be null!");
Assert.notNull(expiration, "Expiration must not be null!");
Assert.notNull(option, "Option must not be null!");
- Assert.isTrue(keepTtl, "KEEPTTL is currently not supported in jedis!");
SetParams setParams = JedisConverters.toSetCommandExPxArgument(expiration,
JedisConverters.toSetCommandNxXxArgument(option));
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java
index b7f00edd3..a92b4a4f1 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java
@@ -533,6 +533,24 @@ abstract public class JedisConverters extends Converters {
SetParams paramsToUse = params == null ? SetParams.setParams() : params;
+ if (expiration.isKeepTtl()) {
+
+ // TODO: remove once jedis supports KEEPTTL (https://github.com/xetorthio/jedis/issues/2248)
+ return new SetParams() {
+
+ @Override
+ public byte[][] getByteParams(byte[]... args) {
+
+ ArrayList byteParams = new ArrayList<>();
+ for (byte[] arg : paramsToUse.getByteParams(args)) {
+ byteParams.add(arg);
+ }
+ byteParams.add(SafeEncoder.encode("keepttl"));
+ return byteParams.toArray(new byte[byteParams.size()][]);
+ }
+ };
+ }
+
if (!expiration.isPersistent()) {
if (expiration.getTimeUnit() == TimeUnit.MILLISECONDS) {
return paramsToUse.px(expiration.getExpirationTime());
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisStringCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisStringCommands.java
index 44c654ec6..917938bbf 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisStringCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisStringCommands.java
@@ -153,20 +153,11 @@ class JedisStringCommands implements RedisStringCommands {
*/
@Override
public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption option) {
- return set(key, value, expiration, option, false);
- }
- /*
- * (non-Javadoc)
- * @see org.springframework.data.redis.connection.RedisStringCommands#set(byte[], byte[], org.springframework.data.redis.core.types.Expiration, org.springframework.data.redis.connection.RedisStringCommands.SetOption, boolean)
- */
- @Override
- public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption option, boolean keepTtl) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(value, "Value must not be null!");
Assert.notNull(expiration, "Expiration must not be null!");
Assert.notNull(option, "Option must not be null!");
- Assert.isTrue(keepTtl, "KEEPTTL is currently not supported in jedis!");
SetParams params = JedisConverters.toSetCommandExPxArgument(expiration,
JedisConverters.toSetCommandNxXxArgument(option));
diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java
index aa22c4284..a2d680460 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java
@@ -768,36 +768,26 @@ abstract public class LettuceConverters extends Converters {
* @since 1.7
*/
public static SetArgs toSetArgs(@Nullable Expiration expiration, @Nullable SetOption option) {
- return toSetArgs(expiration, option, false);
- }
-
- /**
- * Converts a given {@link Expiration} and {@link SetOption} to the according {@link SetArgs}.
- *
- * @param expiration can be {@literal null}.
- * @param option can be {@literal null}.
- * @param keepTtl set the value and retain the existing TTL.
- * @since 2.4
- */
- public static SetArgs toSetArgs(@Nullable Expiration expiration, @Nullable SetOption option, boolean keepTtl) {
SetArgs args = new SetArgs();
- if (expiration != null && !expiration.isPersistent()) {
- switch (expiration.getTimeUnit()) {
- case SECONDS:
- args.ex(expiration.getExpirationTime());
- break;
- default:
- args.px(expiration.getConverted(TimeUnit.MILLISECONDS));
- break;
+ if (expiration != null) {
+
+ if (expiration.isKeepTtl()) {
+ args.keepttl();
+ } else if (!expiration.isPersistent()) {
+
+ switch (expiration.getTimeUnit()) {
+ case SECONDS:
+ args.ex(expiration.getExpirationTime());
+ break;
+ default:
+ args.px(expiration.getConverted(TimeUnit.MILLISECONDS));
+ break;
+ }
}
}
- if (keepTtl) {
- args.keepttl();
- }
-
if (option != null) {
switch (option) {
diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java
index f60374898..6788d22c2 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java
@@ -157,15 +157,7 @@ class LettuceStringCommands implements RedisStringCommands {
*/
@Override
public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption option) {
- return set(key, value, expiration, option, false);
- }
- /*
- * (non-Javadoc)
- * @see org.springframework.data.redis.connection.RedisStringCommands#set(byte[], byte[], org.springframework.data.redis.core.types.Expiration, org.springframework.data.redis.connection.RedisStringCommands.SetOption, boolean)
- */
- @Override
- public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption option, boolean keepTtl) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(value, "Value must not be null!");
Assert.notNull(expiration, "Expiration must not be null!");
@@ -174,18 +166,18 @@ class LettuceStringCommands implements RedisStringCommands {
try {
if (isPipelined()) {
pipeline(connection.newLettuceResult(
- getAsyncConnection().set(key, value, LettuceConverters.toSetArgs(expiration, option, keepTtl)),
+ getAsyncConnection().set(key, value, LettuceConverters.toSetArgs(expiration, option)),
Converters.stringToBooleanConverter(), () -> false));
return null;
}
if (isQueueing()) {
transaction(connection.newLettuceResult(
- getAsyncConnection().set(key, value, LettuceConverters.toSetArgs(expiration, option, keepTtl)),
+ getAsyncConnection().set(key, value, LettuceConverters.toSetArgs(expiration, option)),
Converters.stringToBooleanConverter(), () -> false));
return null;
}
return Converters
- .stringToBoolean(getConnection().set(key, value, LettuceConverters.toSetArgs(expiration, option, keepTtl)));
+ .stringToBoolean(getConnection().set(key, value, LettuceConverters.toSetArgs(expiration, option)));
} catch (Exception ex) {
throw convertLettuceAccessException(ex);
}
diff --git a/src/main/java/org/springframework/data/redis/core/types/Expiration.java b/src/main/java/org/springframework/data/redis/core/types/Expiration.java
index 94b208e4a..05c6616b6 100644
--- a/src/main/java/org/springframework/data/redis/core/types/Expiration.java
+++ b/src/main/java/org/springframework/data/redis/core/types/Expiration.java
@@ -115,6 +115,19 @@ public class Expiration {
return new Expiration(expirationTime, TimeUnit.MILLISECONDS);
}
+ /**
+ * Obtain an {@link Expiration} that indicates to keep the existing one. Eg. when sending a {@code SET} command.
+ *
+ * NOTE: Please follow the documentation of the individual commands to see if {@link #keepTtl()} is
+ * applicable.
+ *
+ * @return never {@literal null}.
+ * @since 2.4
+ */
+ public static Expiration keepTtl() {
+ return KeepTtl.INSTANCE;
+ }
+
/**
* Creates new {@link Expiration} with the provided {@link TimeUnit}. Greater units than {@link TimeUnit#SECONDS} are
* converted to {@link TimeUnit#SECONDS}. Units smaller than {@link TimeUnit#MILLISECONDS} are converted to
@@ -175,4 +188,30 @@ public class Expiration {
public boolean isPersistent() {
return expirationTime == -1;
}
+
+ /**
+ * @return {@literal true} if {@link Expiration} of existing key should not be modified.
+ * @since 2.4
+ */
+ public boolean isKeepTtl() {
+ return false;
+ }
+
+ /**
+ * @author Christoph Strobl
+ * @since 2.4
+ */
+ private static class KeepTtl extends Expiration {
+
+ static KeepTtl INSTANCE = new KeepTtl();
+
+ private KeepTtl() {
+ super(-2, null);
+ }
+
+ @Override
+ public boolean isKeepTtl() {
+ return true;
+ }
+ }
}
diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java
index f8c86978d..49aa2fbfd 100644
--- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java
+++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java
@@ -179,6 +179,20 @@ public abstract class AbstractConnectionIntegrationTests {
assertThat(waitFor(new KeyExpired("exp"), 3000l)).isTrue();
}
+ @Test // DATAREDIS-1103
+ public void testSetWithKeepTTL() {
+
+ actual.add(connection.set("exp", "true"));
+ actual.add(connection.expire("exp", 10));
+ actual.add(connection.set("exp", "changed", Expiration.keepTtl(), SetOption.upsert()));
+ actual.add(connection.ttl("exp"));
+
+ List