diff --git a/pom.xml b/pom.xml
index fd73e23ebd..afbe1266d1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-redis
- 3.5.0-SNAPSHOT
+ 3.5.0-GH-3114-SNAPSHOT
Spring Data Redis
Spring Data module for Redis
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 831a46ece2..c2ea198d8b 100644
--- a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java
@@ -31,7 +31,6 @@
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.RedisSystemException;
-import org.springframework.data.redis.connection.Hash.FieldExpirationOptions;
import org.springframework.data.redis.connection.convert.Converters;
import org.springframework.data.redis.connection.convert.ListConverter;
import org.springframework.data.redis.connection.convert.MapConverter;
@@ -359,13 +358,13 @@ public Long exists(byte[]... keys) {
}
@Override
- public Boolean expire(byte[] key, long seconds) {
- return convertAndReturn(delegate.expire(key, seconds), Converters.identityConverter());
+ public Boolean expire(byte[] key, long seconds, ExpirationOptions.Condition condition) {
+ return convertAndReturn(delegate.expire(key, seconds, condition), Converters.identityConverter());
}
@Override
- public Boolean expireAt(byte[] key, long unixTime) {
- return convertAndReturn(delegate.expireAt(key, unixTime), Converters.identityConverter());
+ public Boolean expireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition) {
+ return convertAndReturn(delegate.expireAt(key, unixTime, condition), Converters.identityConverter());
}
@Override
@@ -1308,13 +1307,13 @@ public Long zLexCount(String key, org.springframework.data.domain.Range
}
@Override
- public Boolean pExpire(byte[] key, long millis) {
- return convertAndReturn(delegate.pExpire(key, millis), Converters.identityConverter());
+ public Boolean pExpire(byte[] key, long millis, ExpirationOptions.Condition condition) {
+ return convertAndReturn(delegate.pExpire(key, millis, condition), Converters.identityConverter());
}
@Override
- public Boolean pExpireAt(byte[] key, long unixTimeInMillis) {
- return convertAndReturn(delegate.pExpireAt(key, unixTimeInMillis), Converters.identityConverter());
+ public Boolean pExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition) {
+ return convertAndReturn(delegate.pExpireAt(key, unixTimeInMillis, condition), Converters.identityConverter());
}
@Override
@@ -1497,13 +1496,13 @@ public Boolean exists(String key) {
}
@Override
- public Boolean expire(String key, long seconds) {
- return expire(serialize(key), seconds);
+ public Boolean expire(String key, long seconds, ExpirationOptions.Condition condition) {
+ return expire(serialize(key), seconds, condition);
}
@Override
- public Boolean expireAt(String key, long unixTime) {
- return expireAt(serialize(key), unixTime);
+ public Boolean expireAt(String key, long unixTime, ExpirationOptions.Condition condition) {
+ return expireAt(serialize(key), unixTime, condition);
}
@Override
@@ -2491,13 +2490,13 @@ public Object execute(String command, String... args) {
}
@Override
- public Boolean pExpire(String key, long millis) {
- return pExpire(serialize(key), millis);
+ public Boolean pExpire(String key, long millis, ExpirationOptions.Condition condition) {
+ return pExpire(serialize(key), millis, condition);
}
@Override
- public Boolean pExpireAt(String key, long unixTimeInMillis) {
- return pExpireAt(serialize(key), unixTimeInMillis);
+ public Boolean pExpireAt(String key, long unixTimeInMillis, ExpirationOptions.Condition condition) {
+ return pExpireAt(serialize(key), unixTimeInMillis, condition);
}
@Override
@@ -2581,29 +2580,29 @@ public Long hStrLen(byte[] key, byte[] field) {
return convertAndReturn(delegate.hStrLen(key, field), Converters.identityConverter());
}
- public @Nullable List applyExpiration(byte[] key,
+ public @Nullable List applyHashFieldExpiration(byte[] key,
org.springframework.data.redis.core.types.Expiration expiration,
- FieldExpirationOptions options, byte[]... fields) {
- return this.delegate.applyExpiration(key, expiration, options, fields);
+ ExpirationOptions options, byte[]... fields) {
+ return this.delegate.applyHashFieldExpiration(key, expiration, options, fields);
}
@Override
- public List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condition condition, byte[]... fields) {
+ public List hExpire(byte[] key, long seconds, ExpirationOptions.Condition condition, byte[]... fields) {
return this.delegate.hExpire(key, seconds, condition, fields);
}
@Override
- public List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condition condition, byte[]... fields) {
+ public List hpExpire(byte[] key, long millis, ExpirationOptions.Condition condition, byte[]... fields) {
return this.delegate.hpExpire(key, millis, condition, fields);
}
@Override
- public List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Condition condition, byte[]... fields) {
+ public List hExpireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition, byte[]... fields) {
return this.delegate.hExpireAt(key, unixTime, condition, fields);
}
@Override
- public List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions.Condition condition,
+ public List hpExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition,
byte[]... fields) {
return this.delegate.hpExpireAt(key, unixTimeInMillis, condition, fields);
}
@@ -2630,27 +2629,27 @@ public List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields) {
public @Nullable List applyExpiration(String key,
org.springframework.data.redis.core.types.Expiration expiration,
- FieldExpirationOptions options, String... fields) {
- return applyExpiration(serialize(key), expiration, options, serializeMulti(fields));
+ ExpirationOptions options, String... fields) {
+ return this.applyHashFieldExpiration(serialize(key), expiration, options, serializeMulti(fields));
}
@Override
- public List hExpire(String key, long seconds, FieldExpirationOptions.Condition condition, String... fields) {
+ public List hExpire(String key, long seconds, ExpirationOptions.Condition condition, String... fields) {
return hExpire(serialize(key), seconds, condition, serializeMulti(fields));
}
@Override
- public List hpExpire(String key, long millis, FieldExpirationOptions.Condition condition, String... fields) {
+ public List hpExpire(String key, long millis, ExpirationOptions.Condition condition, String... fields) {
return hpExpire(serialize(key), millis, condition, serializeMulti(fields));
}
@Override
- public List hExpireAt(String key, long unixTime, FieldExpirationOptions.Condition condition, String... fields) {
+ public List hExpireAt(String key, long unixTime, ExpirationOptions.Condition condition, String... fields) {
return hExpireAt(serialize(key), unixTime, condition, serializeMulti(fields));
}
@Override
- public List hpExpireAt(String key, long unixTimeInMillis, FieldExpirationOptions.Condition condition,
+ public List hpExpireAt(String key, long unixTimeInMillis, ExpirationOptions.Condition condition,
String... fields) {
return hpExpireAt(serialize(key), unixTimeInMillis, condition, serializeMulti(fields));
}
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 8190caa6a0..f3be7ab276 100644
--- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
@@ -28,7 +28,6 @@
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Point;
-import org.springframework.data.redis.connection.Hash.FieldExpirationOptions;
import org.springframework.data.redis.connection.stream.ByteRecord;
import org.springframework.data.redis.connection.stream.Consumer;
import org.springframework.data.redis.connection.stream.MapRecord;
@@ -162,7 +161,14 @@ default Boolean renameNX(byte[] sourceKey, byte[] targetKey) {
@Override
@Deprecated
default Boolean expire(byte[] key, long seconds) {
- return keyCommands().expire(key, seconds);
+ return keyCommands().expire(key, seconds, ExpirationOptions.Condition.ALWAYS);
+ }
+
+ /** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
+ @Override
+ @Deprecated
+ default Boolean expire(byte[] key, long seconds, ExpirationOptions.Condition condition) {
+ return keyCommands().expire(key, seconds, condition);
}
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
@@ -204,21 +210,42 @@ default Long pTtl(byte[] key, TimeUnit timeUnit) {
@Override
@Deprecated
default Boolean pExpire(byte[] key, long millis) {
- return keyCommands().pExpire(key, millis);
+ return keyCommands().pExpire(key, millis, ExpirationOptions.Condition.ALWAYS);
+ }
+
+ /** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
+ @Override
+ @Deprecated
+ default Boolean pExpire(byte[] key, long millis, ExpirationOptions.Condition condition) {
+ return keyCommands().pExpire(key, millis, condition);
}
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
@Override
@Deprecated
default Boolean pExpireAt(byte[] key, long unixTimeInMillis) {
- return keyCommands().pExpireAt(key, unixTimeInMillis);
+ return keyCommands().pExpireAt(key, unixTimeInMillis, ExpirationOptions.Condition.ALWAYS);
+ }
+
+ /** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
+ @Override
+ @Deprecated
+ default Boolean pExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition) {
+ return keyCommands().pExpireAt(key, unixTimeInMillis, condition);
}
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
@Override
@Deprecated
default Boolean expireAt(byte[] key, long unixTime) {
- return keyCommands().expireAt(key, unixTime);
+ return keyCommands().expireAt(key, unixTime, ExpirationOptions.Condition.ALWAYS);
+ }
+
+ /** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
+ @Override
+ @Deprecated
+ default Boolean expireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition) {
+ return keyCommands().expireAt(key, unixTime, condition);
}
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
@@ -1483,13 +1510,13 @@ default Long hStrLen(byte[] key, byte[] field) {
@Override
@Deprecated
default List hExpire(byte[] key, long seconds, byte[]... fields) {
- return hashCommands().hExpire(key, seconds, FieldExpirationOptions.Condition.ALWAYS, fields);
+ return hashCommands().hExpire(key, seconds, ExpirationOptions.Condition.ALWAYS, fields);
}
/** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */
@Override
@Deprecated
- default List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condition condition, byte[]... fields) {
+ default List hExpire(byte[] key, long seconds, ExpirationOptions.Condition condition, byte[]... fields) {
return hashCommands().hExpire(key, seconds, condition, fields);
}
@@ -1497,13 +1524,13 @@ default List hExpire(byte[] key, long seconds, FieldExpirationOptions.Cond
@Override
@Deprecated
default List hpExpire(byte[] key, long millis, byte[]... fields) {
- return hashCommands().hpExpire(key, millis, FieldExpirationOptions.Condition.ALWAYS, fields);
+ return hashCommands().hpExpire(key, millis, ExpirationOptions.Condition.ALWAYS, fields);
}
/** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */
@Override
@Deprecated
- default List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condition condition, byte[]... fields) {
+ default List hpExpire(byte[] key, long millis, ExpirationOptions.Condition condition, byte[]... fields) {
return hashCommands().hpExpire(key, millis, condition, fields);
}
@@ -1511,13 +1538,13 @@ default List hpExpire(byte[] key, long millis, FieldExpirationOptions.Cond
@Override
@Deprecated
default List hExpireAt(byte[] key, long unixTime, byte[]... fields) {
- return hashCommands().hExpireAt(key, unixTime, FieldExpirationOptions.Condition.ALWAYS, fields);
+ return hashCommands().hExpireAt(key, unixTime, ExpirationOptions.Condition.ALWAYS, fields);
}
/** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */
@Override
@Deprecated
- default List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Condition condition,
+ default List hExpireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition,
byte[]... fields) {
return hashCommands().hExpireAt(key, unixTime, condition, fields);
}
@@ -1526,13 +1553,13 @@ default List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.C
@Override
@Deprecated
default List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields) {
- return hashCommands().hpExpireAt(key, unixTimeInMillis, FieldExpirationOptions.Condition.ALWAYS, fields);
+ return hashCommands().hpExpireAt(key, unixTimeInMillis, ExpirationOptions.Condition.ALWAYS, fields);
}
/** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */
@Override
@Deprecated
- default List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions.Condition condition,
+ default List hpExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition,
byte[]... fields) {
return hashCommands().hpExpireAt(key, unixTimeInMillis, condition, fields);
}
@@ -1568,10 +1595,10 @@ default List hpTtl(byte[] key, byte[]... fields) {
/** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */
@Override
@Deprecated
- default @Nullable List applyExpiration(byte[] key,
- org.springframework.data.redis.core.types.Expiration expiration, FieldExpirationOptions options,
+ default @Nullable List applyHashFieldExpiration(byte[] key,
+ org.springframework.data.redis.core.types.Expiration expiration, ExpirationOptions options,
byte[]... fields) {
- return hashCommands().applyExpiration(key, expiration, options, fields);
+ return hashCommands().applyHashFieldExpiration(key, expiration, options, fields);
}
// GEO COMMANDS
diff --git a/src/main/java/org/springframework/data/redis/connection/ExpirationOptions.java b/src/main/java/org/springframework/data/redis/connection/ExpirationOptions.java
new file mode 100644
index 0000000000..900f27bd9d
--- /dev/null
+++ b/src/main/java/org/springframework/data/redis/connection/ExpirationOptions.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.redis.connection;
+
+import java.util.Objects;
+
+import org.springframework.lang.Contract;
+import org.springframework.util.ObjectUtils;
+
+/**
+ * Expiration options for Expiation updates.
+ *
+ * @author Christoph Strobl
+ * @author Mark Paluch
+ * @since 3.5
+ */
+public class ExpirationOptions {
+
+ private static final ExpirationOptions NONE = new ExpirationOptions(Condition.ALWAYS);
+ private final Condition condition;
+
+ ExpirationOptions(Condition condition) {
+ this.condition = condition;
+ }
+
+ /**
+ * @return an empty expiration options object.
+ */
+ public static ExpirationOptions none() {
+ return NONE;
+ }
+
+ /**
+ * @return builder for creating {@code FieldExpireOptionsBuilder}.
+ */
+ public static ExpirationOptionsBuilder builder() {
+ return new ExpirationOptionsBuilder();
+ }
+
+ public Condition getCondition() {
+ return condition;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ExpirationOptions that = (ExpirationOptions) o;
+ return ObjectUtils.nullSafeEquals(this.condition, that.condition);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(condition);
+ }
+
+ /**
+ * Builder to build {@link ExpirationOptions}
+ */
+ public static class ExpirationOptionsBuilder {
+
+ private Condition condition = Condition.ALWAYS;
+
+ private ExpirationOptionsBuilder() {}
+
+ /**
+ * Apply to fields that have no expiration.
+ */
+ @Contract("-> this")
+ public ExpirationOptionsBuilder nx() {
+ this.condition = Condition.NX;
+ return this;
+ }
+
+ /**
+ * Apply to fields that have an existing expiration.
+ */
+ @Contract("-> this")
+ public ExpirationOptionsBuilder xx() {
+ this.condition = Condition.XX;
+ return this;
+ }
+
+ /**
+ * Apply to fields when the new expiration is greater than the current one.
+ */
+ @Contract("-> this")
+ public ExpirationOptionsBuilder gt() {
+ this.condition = Condition.GT;
+ return this;
+ }
+
+ /**
+ * Apply to fields when the new expiration is lower than the current one.
+ */
+ @Contract("-> this")
+ public ExpirationOptionsBuilder lt() {
+ this.condition = Condition.LT;
+ return this;
+ }
+
+ public ExpirationOptions build() {
+ return condition == Condition.ALWAYS ? NONE : new ExpirationOptions(condition);
+ }
+
+ }
+
+ /**
+ * Conditions to apply when changing expiration.
+ */
+ public enum Condition {
+
+ /**
+ * Always apply expiration.
+ */
+ ALWAYS,
+
+ /**
+ * Set expiration only when the field has no expiration.
+ */
+ NX,
+
+ /**
+ * Set expiration only when the field has an existing expiration.
+ */
+ XX,
+
+ /**
+ * Set expiration only when the new expiration is greater than current one.
+ */
+ GT,
+
+ /**
+ * Set expiration only when the new expiration is greater than current one.
+ */
+ LT
+
+ }
+
+}
diff --git a/src/main/java/org/springframework/data/redis/connection/Hash.java b/src/main/java/org/springframework/data/redis/connection/Hash.java
deleted file mode 100644
index 51e326dd2b..0000000000
--- a/src/main/java/org/springframework/data/redis/connection/Hash.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright 2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.data.redis.connection;
-
-import java.util.Objects;
-
-import org.springframework.lang.Contract;
-import org.springframework.util.ObjectUtils;
-
-/**
- * Types for interacting with Hash data structures.
- *
- * @author Christoph Strobl
- * @since 3.5
- */
-public interface Hash {
-
- /**
- * Expiration options for Hash Expiation updates.
- */
- class FieldExpirationOptions {
-
- private static final FieldExpirationOptions NONE = new FieldExpirationOptions(Condition.ALWAYS);
- private final Condition condition;
-
- FieldExpirationOptions(Condition condition) {
- this.condition = condition;
- }
-
- public static FieldExpirationOptions none() {
- return NONE;
- }
-
- public static FieldExpireOptionsBuilder builder() {
- return new FieldExpireOptionsBuilder();
- }
-
- public Condition getCondition() {
- return condition;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- FieldExpirationOptions that = (FieldExpirationOptions) o;
- return ObjectUtils.nullSafeEquals(this.condition, that.condition);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(condition);
- }
-
- public static class FieldExpireOptionsBuilder {
-
- private Condition condition = Condition.ALWAYS;
-
- @Contract("-> this")
- public FieldExpireOptionsBuilder nx() {
- this.condition = Condition.NX;
- return this;
- }
-
- @Contract("-> this")
- public FieldExpireOptionsBuilder xx() {
- this.condition = Condition.XX;
- return this;
- }
-
- @Contract("-> this")
- public FieldExpireOptionsBuilder gt() {
- this.condition = Condition.GT;
- return this;
- }
-
- @Contract("-> this")
- public FieldExpireOptionsBuilder lt() {
- this.condition = Condition.LT;
- return this;
- }
-
- public FieldExpirationOptions build() {
- return condition == Condition.ALWAYS ? NONE : new FieldExpirationOptions(condition);
- }
-
- }
-
- public enum Condition {
-
- /**
- * Always apply expiration.
- */
- ALWAYS,
-
- /**
- * Set expiration only when the field has no expiration.
- */
- NX,
-
- /**
- * Set expiration only when the field has an existing expiration.
- */
- XX,
-
- /**
- * Set expiration only when the new expiration is greater than current one.
- */
- GT,
-
- /**
- * Set expiration only when the new expiration is greater than current one.
- */
- LT
-
- }
-
- }
-
-}
diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java
index c463737747..385b652f2b 100644
--- a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java
@@ -31,7 +31,6 @@
import org.reactivestreams.Publisher;
import org.springframework.dao.InvalidDataAccessApiUsageException;
-import org.springframework.data.redis.connection.Hash.FieldExpirationOptions;
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
import org.springframework.data.redis.connection.ReactiveRedisConnection.Command;
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
@@ -851,13 +850,13 @@ default Mono hStrLen(ByteBuffer key, ByteBuffer field) {
/**
* @since 3.5
*/
- class ExpireCommand extends HashFieldsCommand {
+ class HashExpireCommand extends HashFieldsCommand {
private final Expiration expiration;
- private final FieldExpirationOptions options;
+ private final ExpirationOptions options;
- private ExpireCommand(@Nullable ByteBuffer key, List fields, Expiration expiration,
- FieldExpirationOptions options) {
+ private HashExpireCommand(@Nullable ByteBuffer key, List fields, Expiration expiration,
+ ExpirationOptions options) {
super(key, fields);
@@ -866,52 +865,52 @@ private ExpireCommand(@Nullable ByteBuffer key, List fields, Expirat
}
/**
- * Creates a new {@link ExpireCommand}.
+ * Creates a new {@link HashExpireCommand}.
*
* @param fields the {@code field} names to apply expiration to
* @param timeout the actual timeout
* @param unit the unit of measure for the {@code timeout}.
- * @return new instance of {@link ExpireCommand}.
+ * @return new instance of {@link HashExpireCommand}.
*/
- public static ExpireCommand expire(List fields, long timeout, TimeUnit unit) {
+ public static HashExpireCommand expire(List fields, long timeout, TimeUnit unit) {
Assert.notNull(fields, "Field must not be null");
return expire(fields, Expiration.from(timeout, unit));
}
/**
- * Creates a new {@link ExpireCommand}.
+ * Creates a new {@link HashExpireCommand}.
*
* @param fields the {@code field} names to apply expiration to.
* @param ttl the actual timeout.
- * @return new instance of {@link ExpireCommand}.
+ * @return new instance of {@link HashExpireCommand}.
*/
- public static ExpireCommand expire(List fields, Duration ttl) {
+ public static HashExpireCommand expire(List fields, Duration ttl) {
Assert.notNull(fields, "Field must not be null");
return expire(fields, Expiration.from(ttl));
}
/**
- * Creates a new {@link ExpireCommand}.
+ * Creates a new {@link HashExpireCommand}.
*
* @param fields the {@code field} names to apply expiration to
* @param expiration the {@link Expiration} to apply to the given {@literal fields}.
- * @return new instance of {@link ExpireCommand}.
+ * @return new instance of {@link HashExpireCommand}.
*/
- public static ExpireCommand expire(List fields, Expiration expiration) {
- return new ExpireCommand(null, fields, expiration, FieldExpirationOptions.none());
+ public static HashExpireCommand expire(List fields, Expiration expiration) {
+ return new HashExpireCommand(null, fields, expiration, ExpirationOptions.none());
}
/**
- * Creates a new {@link ExpireCommand}.
+ * Creates a new {@link HashExpireCommand}.
*
* @param fields the {@code field} names to apply expiration to
* @param ttl the unix point in time when to expire the given {@literal fields}.
* @param precision can be {@link TimeUnit#SECONDS} or {@link TimeUnit#MILLISECONDS}.
- * @return new instance of {@link ExpireCommand}.
+ * @return new instance of {@link HashExpireCommand}.
*/
- public static ExpireCommand expireAt(List fields, Instant ttl, TimeUnit precision) {
+ public static HashExpireCommand expireAt(List fields, Instant ttl, TimeUnit precision) {
if (precision.compareTo(TimeUnit.MILLISECONDS) > 0) {
return expire(fields, Expiration.unixTimestamp(ttl.getEpochSecond(), TimeUnit.SECONDS));
@@ -922,25 +921,25 @@ public static ExpireCommand expireAt(List fields, Instant ttl, TimeU
/**
* @param key the {@literal key} from which to expire the {@literal fields} from.
- * @return new instance of {@link ExpireCommand}.
+ * @return new instance of {@link HashExpireCommand}.
*/
- public ExpireCommand from(ByteBuffer key) {
- return new ExpireCommand(key, getFields(), expiration, options);
+ public HashExpireCommand from(ByteBuffer key) {
+ return new HashExpireCommand(key, getFields(), expiration, options);
}
/**
* @param options additional options to be sent along with the command.
- * @return new instance of {@link ExpireCommand}.
+ * @return new instance of {@link HashExpireCommand}.
*/
- public ExpireCommand withOptions(FieldExpirationOptions options) {
- return new ExpireCommand(getKey(), getFields(), getExpiration(), options);
+ public HashExpireCommand withOptions(ExpirationOptions options) {
+ return new HashExpireCommand(getKey(), getFields(), getExpiration(), options);
}
public Expiration getExpiration() {
return expiration;
}
- public FieldExpirationOptions getOptions() {
+ public ExpirationOptions getOptions() {
return options;
}
}
@@ -983,7 +982,7 @@ default Flux hExpire(ByteBuffer key, Duration duration, List f
Assert.notNull(duration, "Duration must not be null");
- return applyExpiration(Flux.just(ExpireCommand.expire(fields, duration).from(key)))
+ return applyHashFieldExpiration(Flux.just(HashExpireCommand.expire(fields, duration).from(key)))
.mapNotNull(NumericResponse::getOutput);
}
@@ -999,7 +998,7 @@ default Flux hExpire(ByteBuffer key, Duration duration, List f
* @since 3.5
* @see Redis Documentation: HEXPIRE
*/
- Flux> applyExpiration(Publisher commands);
+ Flux> applyHashFieldExpiration(Publisher commands);
/**
* Expire a given {@literal field} after a given {@link Duration} of time, measured in milliseconds, has passed.
@@ -1039,8 +1038,8 @@ default Flux hpExpire(ByteBuffer key, Duration duration, List
Assert.notNull(duration, "Duration must not be null");
- return applyExpiration(Flux.just(new ExpireCommand(key, fields,
- Expiration.from(duration.toMillis(), TimeUnit.MILLISECONDS), FieldExpirationOptions.none())))
+ return applyHashFieldExpiration(Flux.just(new HashExpireCommand(key, fields,
+ Expiration.from(duration.toMillis(), TimeUnit.MILLISECONDS), ExpirationOptions.none())))
.mapNotNull(NumericResponse::getOutput);
}
@@ -1083,7 +1082,7 @@ default Flux hExpireAt(ByteBuffer key, Instant expireAt, List
Assert.notNull(expireAt, "Duration must not be null");
- return applyExpiration(Flux.just(ExpireCommand.expireAt(fields, expireAt, TimeUnit.SECONDS).from(key)))
+ return applyHashFieldExpiration(Flux.just(HashExpireCommand.expireAt(fields, expireAt, TimeUnit.SECONDS).from(key)))
.mapNotNull(NumericResponse::getOutput);
}
@@ -1126,7 +1125,8 @@ default Flux hpExpireAt(ByteBuffer key, Instant expireAt, List
Assert.notNull(expireAt, "Duration must not be null");
- return applyExpiration(Flux.just(ExpireCommand.expireAt(fields, expireAt, TimeUnit.MILLISECONDS).from(key)))
+ return applyHashFieldExpiration(
+ Flux.just(HashExpireCommand.expireAt(fields, expireAt, TimeUnit.MILLISECONDS).from(key)))
.mapNotNull(NumericResponse::getOutput);
}
diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java
index 91b81640c1..62be2393ef 100644
--- a/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java
@@ -25,6 +25,7 @@
import java.util.List;
import org.reactivestreams.Publisher;
+
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
@@ -32,6 +33,7 @@
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
import org.springframework.data.redis.core.KeyScanOptions;
import org.springframework.data.redis.core.ScanOptions;
+import org.springframework.data.redis.core.types.Expiration;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -513,13 +515,35 @@ default Mono mUnlink(List keys) {
*/
class ExpireCommand extends KeyCommand {
- private @Nullable Duration timeout;
+ private final Expiration expiration;
+ private final ExpirationOptions options;
+
+ private ExpireCommand(ByteBuffer key, Duration timeout) {
+ this(key, Expiration.from(timeout), ExpirationOptions.none());
+ }
- private ExpireCommand(ByteBuffer key, @Nullable Duration timeout) {
+ private ExpireCommand(@Nullable ByteBuffer key, Expiration expiration, ExpirationOptions options) {
super(key);
- this.timeout = timeout;
+ this.expiration = expiration;
+ this.options = options;
+ }
+
+ /**
+ * Creates a new {@link ExpireCommand} given a {@link ByteBuffer key} and {@link Expiration}.
+ *
+ * @param key must not be {@literal null}.
+ * @param expiration must not be {@literal null}.
+ * @return a new {@link ExpireCommand} for {@link ByteBuffer key} and {@link Expiration}.
+ * @since 3.5
+ */
+ public static ExpireCommand expire(ByteBuffer key, Expiration expiration) {
+
+ Assert.notNull(key, "Key must not be null");
+ Assert.notNull(expiration, "Expiration must not be null");
+
+ return new ExpireCommand(key, expiration, ExpirationOptions.none());
}
/**
@@ -532,7 +556,7 @@ public static ExpireCommand key(ByteBuffer key) {
Assert.notNull(key, "Key must not be null");
- return new ExpireCommand(key, null);
+ return new ExpireCommand(key, Expiration.persistent(), ExpirationOptions.none());
}
/**
@@ -545,7 +569,21 @@ public ExpireCommand timeout(Duration timeout) {
Assert.notNull(timeout, "Timeout must not be null");
- return new ExpireCommand(getKey(), timeout);
+ return new ExpireCommand(getKey(), Expiration.from(timeout), options);
+ }
+
+ /**
+ * Applies the {@literal timeout}. Constructs a new command instance with all previously configured properties.
+ *
+ * @param timeout must not be {@literal null}.
+ * @return a new {@link ExpireCommand} with {@literal timeout} applied.
+ * @since 3.5
+ */
+ public ExpireCommand expire(Duration timeout) {
+
+ Assert.notNull(timeout, "Timeout must not be null");
+
+ return new ExpireCommand(getKey(), Expiration.from(timeout), options);
}
/**
@@ -553,10 +591,50 @@ public ExpireCommand timeout(Duration timeout) {
*/
@Nullable
public Duration getTimeout() {
- return timeout;
+
+ if (expiration.isUnixTimestamp() || expiration.isPersistent()) {
+ return null;
+ }
+
+ return Duration.ofMillis(expiration.getExpirationTimeInMilliseconds());
}
+
+ /**
+ * @param options additional options to be sent along with the command.
+ * @return new instance of {@link ExpireCommand}.
+ * @since 3.5
+ */
+ public ExpireCommand withOptions(ExpirationOptions options) {
+ return new ExpireCommand(getKey(), getExpiration(), options);
+ }
+
+ public Expiration getExpiration() {
+ return expiration;
+ }
+
+ public ExpirationOptions getOptions() {
+ return options;
+ }
+
}
+ /**
+ * Expire a {@link List} of {@literal field} after a given {@link Duration} of time, measured in milliseconds, has
+ * passed.
+ *
+ * @param commands must not be {@literal null}.
+ * @return a {@link Flux} emitting the expiration results one by one, {@literal true} if the timeout was set or
+ * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was
+ * skipped because of the provided arguments.
+ * @since 3.5
+ * @see Redis Documentation: EXPIRE
+ * @see Redis Documentation: PEXPIRE
+ * @see Redis Documentation: EXPIREAT
+ * @see Redis Documentation: PEXPIREAT
+ * @see Redis Documentation: PERSIST
+ */
+ Flux> applyExpiration(Publisher commands);
+
/**
* Set time to live for given {@code key} in seconds.
*
@@ -581,7 +659,9 @@ default Mono expire(ByteBuffer key, Duration timeout) {
* result.
* @see Redis Documentation: EXPIRE
*/
- Flux> expire(Publisher commands);
+ default Flux> expire(Publisher commands) {
+ return applyExpiration(commands);
+ }
/**
* Set time to live for given {@code key} in milliseconds.
@@ -607,7 +687,9 @@ default Mono pExpire(ByteBuffer key, Duration timeout) {
* result.
* @see Redis Documentation: PEXPIRE
*/
- Flux> pExpire(Publisher commands);
+ default Flux> pExpire(Publisher commands) {
+ return applyExpiration(commands);
+ }
/**
* {@code EXPIREAT}/{@code PEXPIREAT} command parameters.
@@ -619,12 +701,18 @@ default Mono pExpire(ByteBuffer key, Duration timeout) {
class ExpireAtCommand extends KeyCommand {
private @Nullable Instant expireAt;
+ private final ExpirationOptions options;
- private ExpireAtCommand(ByteBuffer key, @Nullable Instant expireAt) {
+ private ExpireAtCommand(ByteBuffer key, Instant expireAt) {
+ this(key, expireAt, ExpirationOptions.none());
+ }
+
+ private ExpireAtCommand(@Nullable ByteBuffer key, Instant expireAt, ExpirationOptions options) {
super(key);
this.expireAt = expireAt;
+ this.options = options;
}
/**
@@ -653,6 +741,15 @@ public ExpireAtCommand timeout(Instant expireAt) {
return new ExpireAtCommand(getKey(), expireAt);
}
+ /**
+ * @param options additional options to be sent along with the command.
+ * @return new instance of {@link ExpireAtCommand}.
+ * @since 3.5
+ */
+ public ExpireAtCommand withOptions(ExpirationOptions options) {
+ return new ExpireAtCommand(getKey(), getExpireAt(), options);
+ }
+
/**
* @return can be {@literal null}.
*/
@@ -660,6 +757,11 @@ public ExpireAtCommand timeout(Instant expireAt) {
public Instant getExpireAt() {
return expireAt;
}
+
+ public ExpirationOptions getOptions() {
+ return options;
+ }
+
}
/**
diff --git a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java
index d038708526..cd99e4a516 100644
--- a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java
@@ -21,7 +21,6 @@
import java.util.Set;
import java.util.concurrent.TimeUnit;
-import org.springframework.data.redis.connection.Hash.FieldExpirationOptions;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.lang.Nullable;
@@ -257,7 +256,7 @@ public interface RedisHashCommands {
/**
* Apply a given {@link org.springframework.data.redis.core.types.Expiration} to the given {@literal fields}.
- *
+ *
* @param key must not be {@literal null}.
* @param expiration the {@link org.springframework.data.redis.core.types.Expiration} to apply.
* @param fields the names of the {@literal fields} to apply the {@literal expiration} to.
@@ -267,9 +266,9 @@ public interface RedisHashCommands {
* such field;
* @since 3.5
*/
- default @Nullable List applyExpiration(byte[] key,
+ default @Nullable List applyHashFieldExpiration(byte[] key,
org.springframework.data.redis.core.types.Expiration expiration, byte[]... fields) {
- return applyExpiration(key, expiration, FieldExpirationOptions.none(), fields);
+ return applyHashFieldExpiration(key, expiration, ExpirationOptions.none(), fields);
}
/**
@@ -284,14 +283,14 @@ public interface RedisHashCommands {
* @since 3.5
*/
@Nullable
- default List applyExpiration(byte[] key, org.springframework.data.redis.core.types.Expiration expiration,
- FieldExpirationOptions options, byte[]... fields) {
+ default List applyHashFieldExpiration(byte[] key,
+ org.springframework.data.redis.core.types.Expiration expiration, ExpirationOptions options, byte[]... fields) {
if (expiration.isPersistent()) {
return hPersist(key, fields);
}
- if (ObjectUtils.nullSafeEquals(FieldExpirationOptions.none(), options)) {
+ if (ObjectUtils.nullSafeEquals(ExpirationOptions.none(), options)) {
if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) {
if (expiration.isUnixTimestamp()) {
return hpExpireAt(key, expiration.getExpirationTimeInMilliseconds(), fields);
@@ -334,7 +333,7 @@ default List applyExpiration(byte[] key, org.springframework.data.redis.co
*/
@Nullable
default List hExpire(byte[] key, long seconds, byte[]... fields) {
- return hExpire(key, seconds, FieldExpirationOptions.Condition.ALWAYS, fields);
+ return hExpire(key, seconds, ExpirationOptions.Condition.ALWAYS, fields);
}
/**
@@ -372,7 +371,7 @@ default List hExpire(byte[] key, Duration ttl, byte[]... fields) {
* @since 3.5
*/
@Nullable
- List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condition condition, byte[]... fields);
+ List hExpire(byte[] key, long seconds, ExpirationOptions.Condition condition, byte[]... fields);
/**
* Set time to live for given {@code fields} in milliseconds.
@@ -390,7 +389,7 @@ default List hExpire(byte[] key, Duration ttl, byte[]... fields) {
*/
@Nullable
default List hpExpire(byte[] key, long millis, byte[]... fields) {
- return hpExpire(key, millis, FieldExpirationOptions.Condition.ALWAYS, fields);
+ return hpExpire(key, millis, ExpirationOptions.Condition.ALWAYS, fields);
}
/**
@@ -429,7 +428,7 @@ default List hpExpire(byte[] key, Duration ttl, byte[]... fields) {
* @since 3.5
*/
@Nullable
- List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condition condition, byte[]... fields);
+ List hpExpire(byte[] key, long millis, ExpirationOptions.Condition condition, byte[]... fields);
/**
* Set the expiration for given {@code field} as a {@literal UNIX} timestamp.
@@ -446,7 +445,7 @@ default List hpExpire(byte[] key, Duration ttl, byte[]... fields) {
*/
@Nullable
default List hExpireAt(byte[] key, long unixTime, byte[]... fields) {
- return hExpireAt(key, unixTime, FieldExpirationOptions.Condition.ALWAYS, fields);
+ return hExpireAt(key, unixTime, ExpirationOptions.Condition.ALWAYS, fields);
}
/**
@@ -465,7 +464,7 @@ default List hExpireAt(byte[] key, long unixTime, byte[]... fields) {
* @since 3.5
*/
@Nullable
- List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Condition condition, byte[]... fields);
+ List hExpireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition, byte[]... fields);
/**
* Set the expiration for given {@code field} as a {@literal UNIX} timestamp in milliseconds.
@@ -482,7 +481,7 @@ default List hExpireAt(byte[] key, long unixTime, byte[]... fields) {
*/
@Nullable
default List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields) {
- return hpExpireAt(key, unixTimeInMillis, FieldExpirationOptions.Condition.ALWAYS, fields);
+ return hpExpireAt(key, unixTimeInMillis, ExpirationOptions.Condition.ALWAYS, fields);
}
/**
@@ -501,7 +500,7 @@ default List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... field
* @since 3.5
*/
@Nullable
- List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions.Condition condition,
+ List hpExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition,
byte[]... fields);
/**
diff --git a/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java
index 49326637d3..4319dd8705 100644
--- a/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java
@@ -26,6 +26,7 @@
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
+import org.springframework.util.ObjectUtils;
/**
* Key-specific commands supported by Redis.
@@ -181,23 +182,93 @@ default Cursor scan(KeyScanOptions options) {
@Nullable
Boolean renameNX(byte[] oldKey, byte[] newKey);
+ /**
+ * @param key must not be {@literal null}.
+ * @param expiration the {@link org.springframework.data.redis.core.types.Expiration} to apply.
+ * @param options additional options to be sent along with the command.
+ * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or
+ * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was
+ * skipped because of the provided arguments.
+ * @since 3.5
+ * @see Redis Documentation: EXPIRE
+ * @see Redis Documentation: PEXPIRE
+ * @see Redis Documentation: EXPIREAT
+ * @see Redis Documentation: PEXPIREAT
+ * @see Redis Documentation: PERSIST
+ */
+ @Nullable
+ default Boolean applyExpiration(byte[] key, org.springframework.data.redis.core.types.Expiration expiration,
+ ExpirationOptions options) {
+
+ if (expiration.isPersistent()) {
+ return persist(key);
+ }
+
+ if (ObjectUtils.nullSafeEquals(ExpirationOptions.none(), options)) {
+ if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) {
+ if (expiration.isUnixTimestamp()) {
+ return expireAt(key, expiration.getExpirationTimeInMilliseconds());
+ }
+ return expire(key, expiration.getExpirationTimeInMilliseconds());
+ }
+ if (expiration.isUnixTimestamp()) {
+ return expireAt(key, expiration.getExpirationTimeInSeconds());
+ }
+ return expire(key, expiration.getExpirationTimeInSeconds());
+ }
+
+ if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) {
+ if (expiration.isUnixTimestamp()) {
+ return expireAt(key, expiration.getExpirationTimeInMilliseconds(), options.getCondition());
+ }
+
+ return expire(key, expiration.getExpirationTimeInMilliseconds(), options.getCondition());
+ }
+
+ if (expiration.isUnixTimestamp()) {
+ return expireAt(key, expiration.getExpirationTimeInSeconds(), options.getCondition());
+ }
+
+ return expire(key, expiration.getExpirationTimeInSeconds(), options.getCondition());
+ }
+
/**
* Set time to live for given {@code key} in seconds.
*
* @param key must not be {@literal null}.
* @param seconds
- * @return {@literal null} when used in pipeline / transaction.
+ * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or
+ * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was
+ * skipped because of the provided arguments.
+ * @see Redis Documentation: EXPIRE
+ */
+ @Nullable
+ default Boolean expire(byte[] key, long seconds) {
+ return expire(key, seconds, ExpirationOptions.Condition.ALWAYS);
+ }
+
+ /**
+ * Set time to live for given {@code key} in seconds.
+ *
+ * @param key must not be {@literal null}.
+ * @param seconds
+ * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or
+ * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was
+ * skipped because of the provided arguments.
* @see Redis Documentation: EXPIRE
+ * @since 3.5
*/
@Nullable
- Boolean expire(byte[] key, long seconds);
+ Boolean expire(byte[] key, long seconds, ExpirationOptions.Condition condition);
/**
* Set time to live for given {@code key} using {@link Duration#toSeconds() seconds} precision.
*
* @param key must not be {@literal null}.
* @param duration
- * @return {@literal null} when used in pipeline / transaction.
+ * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or
+ * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was
+ * skipped because of the provided arguments.
* @see Redis Documentation: EXPIRE
* @since 3.5
*/
@@ -211,18 +282,38 @@ default Boolean expire(byte[] key, Duration duration) {
*
* @param key must not be {@literal null}.
* @param millis
- * @return {@literal null} when used in pipeline / transaction.
+ * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or
+ * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was
+ * skipped because of the provided arguments.
+ * @see Redis Documentation: PEXPIRE
+ */
+ @Nullable
+ default Boolean pExpire(byte[] key, long millis) {
+ return pExpire(key, millis, ExpirationOptions.Condition.ALWAYS);
+ }
+
+ /**
+ * Set time to live for given {@code key} in milliseconds.
+ *
+ * @param key must not be {@literal null}.
+ * @param millis
+ * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or
+ * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was
+ * skipped because of the provided arguments.
* @see Redis Documentation: PEXPIRE
+ * @since 3.5
*/
@Nullable
- Boolean pExpire(byte[] key, long millis);
+ Boolean pExpire(byte[] key, long millis, ExpirationOptions.Condition condition);
/**
* Set time to live for given {@code key} using {@link Duration#toMillis() milliseconds} precision.
*
* @param key must not be {@literal null}.
* @param duration
- * @return {@literal null} when used in pipeline / transaction.
+ * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or
+ * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was
+ * skipped because of the provided arguments.
* @see Redis Documentation: PEXPIRE
* @since 3.5
*/
@@ -236,11 +327,29 @@ default Boolean pExpire(byte[] key, Duration duration) {
*
* @param key must not be {@literal null}.
* @param unixTime
- * @return {@literal null} when used in pipeline / transaction.
+ * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or
+ * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was
+ * skipped because of the provided arguments.
+ * @see Redis Documentation: EXPIREAT
+ */
+ @Nullable
+ default Boolean expireAt(byte[] key, long unixTime) {
+ return expireAt(key, unixTime, ExpirationOptions.Condition.ALWAYS);
+ }
+
+ /**
+ * Set the expiration for given {@code key} as a {@literal UNIX} timestamp.
+ *
+ * @param key must not be {@literal null}.
+ * @param unixTime
+ * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or
+ * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was
+ * skipped because of the provided arguments.
* @see Redis Documentation: EXPIREAT
+ * @since 3.5
*/
@Nullable
- Boolean expireAt(byte[] key, long unixTime);
+ Boolean expireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition);
/**
* Set the expiration for given {@code key} as a {@literal UNIX} timestamp in {@link Instant#getEpochSecond() seconds}
@@ -248,7 +357,9 @@ default Boolean pExpire(byte[] key, Duration duration) {
*
* @param key must not be {@literal null}.
* @param unixTime
- * @return {@literal null} when used in pipeline / transaction.
+ * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or
+ * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was
+ * skipped because of the provided arguments.
* @see Redis Documentation: EXPIREAT
* @since 3.5
*/
@@ -262,11 +373,29 @@ default Boolean expireAt(byte[] key, Instant unixTime) {
*
* @param key must not be {@literal null}.
* @param unixTimeInMillis
- * @return {@literal null} when used in pipeline / transaction.
+ * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or
+ * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was
+ * skipped because of the provided arguments.
+ * @see Redis Documentation: PEXPIREAT
+ */
+ @Nullable
+ default Boolean pExpireAt(byte[] key, long unixTimeInMillis) {
+ return pExpireAt(key, unixTimeInMillis, ExpirationOptions.Condition.ALWAYS);
+ }
+
+ /**
+ * Set the expiration for given {@code key} as a {@literal UNIX} timestamp in milliseconds.
+ *
+ * @param key must not be {@literal null}.
+ * @param unixTimeInMillis
+ * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or
+ * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was
+ * skipped because of the provided arguments.
* @see Redis Documentation: PEXPIREAT
+ * @since 3.5
*/
@Nullable
- Boolean pExpireAt(byte[] key, long unixTimeInMillis);
+ Boolean pExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition);
/**
* Set the expiration for given {@code key} as a {@literal UNIX} timestamp in {@link Instant#toEpochMilli()
@@ -274,7 +403,9 @@ default Boolean expireAt(byte[] key, Instant unixTime) {
*
* @param key must not be {@literal null}.
* @param unixTime
- * @return {@literal null} when used in pipeline / transaction.
+ * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or
+ * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was
+ * skipped because of the provided arguments.
* @see Redis Documentation: PEXPIREAT
* @since 3.5
*/
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 1069e430c8..dfcc585130 100644
--- a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java
@@ -224,7 +224,22 @@ interface StringTuple extends Tuple {
* @see Redis Documentation: EXPIRE
* @see RedisKeyCommands#expire(byte[], long)
*/
- Boolean expire(String key, long seconds);
+ default Boolean expire(String key, long seconds) {
+ return expire(key, seconds, ExpirationOptions.Condition.ALWAYS);
+ }
+
+ /**
+ * Set time to live for given {@code key} in seconds.
+ *
+ * @param key must not be {@literal null}.
+ * @param condition the condition for expiration, must not be {@literal null}.
+ * @param seconds
+ * @return
+ * @since 3.5
+ * @see Redis Documentation: EXPIRE
+ * @see RedisKeyCommands#expire(byte[], long)
+ */
+ Boolean expire(String key, long seconds, ExpirationOptions.Condition condition);
/**
* Set time to live for given {@code key} in milliseconds.
@@ -235,7 +250,22 @@ interface StringTuple extends Tuple {
* @see Redis Documentation: PEXPIRE
* @see RedisKeyCommands#pExpire(byte[], long)
*/
- Boolean pExpire(String key, long millis);
+ default Boolean pExpire(String key, long millis) {
+ return pExpire(key, millis, ExpirationOptions.Condition.ALWAYS);
+ }
+
+ /**
+ * Set time to live for given {@code key} in milliseconds.
+ *
+ * @param key must not be {@literal null}.
+ * @param millis
+ * @param condition the condition for expiration, must not be {@literal null}.
+ * @return
+ * @since 3.5
+ * @see Redis Documentation: PEXPIRE
+ * @see RedisKeyCommands#pExpire(byte[], long)
+ */
+ Boolean pExpire(String key, long millis, ExpirationOptions.Condition condition);
/**
* Set the expiration for given {@code key} as a {@literal UNIX} timestamp.
@@ -246,7 +276,22 @@ interface StringTuple extends Tuple {
* @see Redis Documentation: EXPIREAT
* @see RedisKeyCommands#expireAt(byte[], long)
*/
- Boolean expireAt(String key, long unixTime);
+ default Boolean expireAt(String key, long unixTime) {
+ return expireAt(key, unixTime, ExpirationOptions.Condition.ALWAYS);
+ }
+
+ /**
+ * Set the expiration for given {@code key} as a {@literal UNIX} timestamp.
+ *
+ * @param key must not be {@literal null}.
+ * @param unixTime
+ * @param condition the condition for expiration, must not be {@literal null}.
+ * @return
+ * @since 3.5
+ * @see Redis Documentation: EXPIREAT
+ * @see RedisKeyCommands#expireAt(byte[], long)
+ */
+ Boolean expireAt(String key, long unixTime, ExpirationOptions.Condition condition);
/**
* Set the expiration for given {@code key} as a {@literal UNIX} timestamp in milliseconds.
@@ -257,7 +302,22 @@ interface StringTuple extends Tuple {
* @see Redis Documentation: PEXPIREAT
* @see RedisKeyCommands#pExpireAt(byte[], long)
*/
- Boolean pExpireAt(String key, long unixTimeInMillis);
+ default Boolean pExpireAt(String key, long unixTimeInMillis) {
+ return pExpireAt(key, unixTimeInMillis, ExpirationOptions.Condition.ALWAYS);
+ }
+
+ /**
+ * Set the expiration for given {@code key} as a {@literal UNIX} timestamp in milliseconds.
+ *
+ * @param key must not be {@literal null}.
+ * @param unixTimeInMillis
+ * @param condition the condition for expiration, must not be {@literal null}.
+ * @return
+ * @since 3.5
+ * @see Redis Documentation: PEXPIREAT
+ * @see RedisKeyCommands#pExpireAt(byte[], long)
+ */
+ Boolean pExpireAt(String key, long unixTimeInMillis, ExpirationOptions.Condition condition);
/**
* Remove the expiration from given {@code key}.
@@ -2348,7 +2408,7 @@ Long zRangeStoreRevByScore(String dstKey, String srcKey,
*/
@Nullable
default List hExpire(String key, long seconds, String... fields) {
- return hExpire(key, seconds, Hash.FieldExpirationOptions.Condition.ALWAYS, fields);
+ return hExpire(key, seconds, ExpirationOptions.Condition.ALWAYS, fields);
}
/**
@@ -2366,7 +2426,7 @@ default List hExpire(String key, long seconds, String... fields) {
* @since 3.5
*/
@Nullable
- List hExpire(String key, long seconds, Hash.FieldExpirationOptions.Condition condition, String... fields);
+ List hExpire(String key, long seconds, ExpirationOptions.Condition condition, String... fields);
/**
* Set time to live for given {@code field} in milliseconds.
@@ -2383,7 +2443,7 @@ default List hExpire(String key, long seconds, String... fields) {
*/
@Nullable
default List hpExpire(String key, long millis, String... fields) {
- return hpExpire(key, millis, Hash.FieldExpirationOptions.Condition.ALWAYS, fields);
+ return hpExpire(key, millis, ExpirationOptions.Condition.ALWAYS, fields);
}
/**
@@ -2401,7 +2461,7 @@ default List hpExpire(String key, long millis, String... fields) {
* @since 3.5
*/
@Nullable
- List hpExpire(String key, long millis, Hash.FieldExpirationOptions.Condition condition, String... fields);
+ List hpExpire(String key, long millis, ExpirationOptions.Condition condition, String... fields);
/**
* Set the expiration for given {@code field} as a {@literal UNIX} timestamp.
@@ -2418,7 +2478,7 @@ default List hpExpire(String key, long millis, String... fields) {
*/
@Nullable
default List hExpireAt(String key, long unixTime, String... fields) {
- return hExpireAt(key, unixTime, Hash.FieldExpirationOptions.Condition.ALWAYS, fields);
+ return hExpireAt(key, unixTime, ExpirationOptions.Condition.ALWAYS, fields);
}
/**
@@ -2436,7 +2496,7 @@ default List hExpireAt(String key, long unixTime, String... fields) {
* @since 3.5
*/
@Nullable
- List hExpireAt(String key, long unixTime, Hash.FieldExpirationOptions.Condition condition, String... fields);
+ List hExpireAt(String key, long unixTime, ExpirationOptions.Condition condition, String... fields);
/**
* Set the expiration for given {@code field} as a {@literal UNIX} timestamp in milliseconds.
@@ -2453,7 +2513,7 @@ default List hExpireAt(String key, long unixTime, String... fields) {
*/
@Nullable
default List hpExpireAt(String key, long unixTimeInMillis, String... fields) {
- return hpExpireAt(key, unixTimeInMillis, Hash.FieldExpirationOptions.Condition.ALWAYS, fields);
+ return hpExpireAt(key, unixTimeInMillis, ExpirationOptions.Condition.ALWAYS, fields);
}
/**
@@ -2471,7 +2531,7 @@ default List hpExpireAt(String key, long unixTimeInMillis, String... field
* @since 3.5
*/
@Nullable
- List hpExpireAt(String key, long unixTimeInMillis, Hash.FieldExpirationOptions.Condition condition,
+ List hpExpireAt(String key, long unixTimeInMillis, ExpirationOptions.Condition condition,
String... fields);
/**
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java
index 1223ab4c06..551a17d153 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java
@@ -27,7 +27,7 @@
import java.util.concurrent.TimeUnit;
import org.springframework.dao.DataAccessException;
-import org.springframework.data.redis.connection.Hash.FieldExpirationOptions;
+import org.springframework.data.redis.connection.ExpirationOptions;
import org.springframework.data.redis.connection.RedisHashCommands;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanCursor;
@@ -291,13 +291,13 @@ protected ScanIteration> doScan(CursorId cursorId, ScanOpt
}
@Override
- public List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condition condition, byte[]... fields) {
+ public List hExpire(byte[] key, long seconds, ExpirationOptions.Condition condition, byte[]... fields) {
Assert.notNull(key, "Key must not be null");
Assert.notNull(fields, "Fields must not be null");
try {
- if (condition == FieldExpirationOptions.Condition.ALWAYS) {
+ if (condition == ExpirationOptions.Condition.ALWAYS) {
return connection.getCluster().hexpire(key, seconds, fields);
}
@@ -308,13 +308,13 @@ public List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condi
}
@Override
- public List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condition condition, byte[]... fields) {
+ public List hpExpire(byte[] key, long millis, ExpirationOptions.Condition condition, byte[]... fields) {
Assert.notNull(key, "Key must not be null");
Assert.notNull(fields, "Fields must not be null");
try {
- if (condition == FieldExpirationOptions.Condition.ALWAYS) {
+ if (condition == ExpirationOptions.Condition.ALWAYS) {
return connection.getCluster().hpexpire(key, millis, fields);
}
@@ -325,13 +325,13 @@ public List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condi
}
@Override
- public List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Condition condition, byte[]... fields) {
+ public List hExpireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition, byte[]... fields) {
Assert.notNull(key, "Key must not be null");
Assert.notNull(fields, "Fields must not be null");
try {
- if (condition == FieldExpirationOptions.Condition.ALWAYS) {
+ if (condition == ExpirationOptions.Condition.ALWAYS) {
return connection.getCluster().hexpireAt(key, unixTime, fields);
}
@@ -342,7 +342,7 @@ public List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Co
}
@Override
- public List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions.Condition condition,
+ public List hpExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition,
byte[]... fields) {
Assert.notNull(key, "Key must not be null");
@@ -350,7 +350,7 @@ public List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationO
try {
- if (condition == FieldExpirationOptions.Condition.ALWAYS) {
+ if (condition == ExpirationOptions.Condition.ALWAYS) {
return connection.getCluster().hpexpireAt(key, unixTimeInMillis, fields);
}
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java
index cb1b07e9c3..559c15bf86 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java
@@ -16,6 +16,7 @@
package org.springframework.data.redis.connection.jedis;
import redis.clients.jedis.Jedis;
+import redis.clients.jedis.args.ExpiryOption;
import redis.clients.jedis.params.RestoreParams;
import redis.clients.jedis.params.ScanParams;
import redis.clients.jedis.resps.ScanResult;
@@ -35,6 +36,7 @@
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.redis.connection.ClusterSlotHashUtil;
import org.springframework.data.redis.connection.DataType;
+import org.springframework.data.redis.connection.ExpirationOptions;
import org.springframework.data.redis.connection.RedisClusterNode;
import org.springframework.data.redis.connection.RedisKeyCommands;
import org.springframework.data.redis.connection.RedisNode;
@@ -274,48 +276,67 @@ public Boolean renameNX(byte[] sourceKey, byte[] targetKey) {
}
@Override
- public Boolean expire(byte[] key, long seconds) {
+ public Boolean expire(byte[] key, long seconds, ExpirationOptions.Condition condition) {
Assert.notNull(key, "Key must not be null");
try {
- return JedisConverters.toBoolean(connection.getCluster().expire(key, seconds));
+ if (condition == ExpirationOptions.Condition.ALWAYS) {
+ return JedisConverters.toBoolean(connection.getCluster().expire(key, seconds));
+ }
+
+ return JedisConverters
+ .toBoolean(connection.getCluster().expire(key, seconds, ExpiryOption.valueOf(condition.name())));
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
}
@Override
- public Boolean pExpire(byte[] key, long millis) {
+ public Boolean pExpire(byte[] key, long millis, ExpirationOptions.Condition condition) {
Assert.notNull(key, "Key must not be null");
try {
- return JedisConverters.toBoolean(connection.getCluster().pexpire(key, millis));
+ if (condition == ExpirationOptions.Condition.ALWAYS) {
+ return JedisConverters.toBoolean(connection.getCluster().pexpire(key, millis));
+ }
+ return JedisConverters
+ .toBoolean(connection.getCluster().pexpire(key, millis, ExpiryOption.valueOf(condition.name())));
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
}
@Override
- public Boolean expireAt(byte[] key, long unixTime) {
+ public Boolean expireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition) {
Assert.notNull(key, "Key must not be null");
try {
- return JedisConverters.toBoolean(connection.getCluster().expireAt(key, unixTime));
+ if (condition == ExpirationOptions.Condition.ALWAYS) {
+ return JedisConverters.toBoolean(connection.getCluster().expireAt(key, unixTime));
+ }
+
+ return JedisConverters
+ .toBoolean(connection.getCluster().expireAt(key, unixTime, ExpiryOption.valueOf(condition.name())));
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
}
@Override
- public Boolean pExpireAt(byte[] key, long unixTimeInMillis) {
+ public Boolean pExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition) {
Assert.notNull(key, "Key must not be null");
try {
- return JedisConverters.toBoolean(connection.getCluster().pexpireAt(key, unixTimeInMillis));
+ if (condition == ExpirationOptions.Condition.ALWAYS) {
+ return JedisConverters.toBoolean(connection.getCluster().pexpireAt(key, unixTimeInMillis));
+ }
+
+ return JedisConverters
+ .toBoolean(connection.getCluster().pexpireAt(key, unixTimeInMillis, ExpiryOption.valueOf(condition.name())));
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java
index 2e83d8aba0..33189c27b4 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java
@@ -29,7 +29,7 @@
import java.util.concurrent.TimeUnit;
import org.springframework.dao.InvalidDataAccessApiUsageException;
-import org.springframework.data.redis.connection.Hash.FieldExpirationOptions;
+import org.springframework.data.redis.connection.ExpirationOptions;
import org.springframework.data.redis.connection.RedisHashCommands;
import org.springframework.data.redis.connection.convert.Converters;
import org.springframework.data.redis.core.Cursor;
@@ -256,9 +256,9 @@ protected void doClose() {
}
@Override
- public List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condition condition, byte[]... fields) {
+ public List hExpire(byte[] key, long seconds, ExpirationOptions.Condition condition, byte[]... fields) {
- if (condition == FieldExpirationOptions.Condition.ALWAYS) {
+ if (condition == ExpirationOptions.Condition.ALWAYS) {
return connection.invoke().just(Jedis::hexpire, PipelineBinaryCommands::hexpire, key, seconds, fields);
}
@@ -267,9 +267,9 @@ public List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condi
}
@Override
- public List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condition condition, byte[]... fields) {
+ public List hpExpire(byte[] key, long millis, ExpirationOptions.Condition condition, byte[]... fields) {
- if (condition == FieldExpirationOptions.Condition.ALWAYS) {
+ if (condition == ExpirationOptions.Condition.ALWAYS) {
return connection.invoke().just(Jedis::hpexpire, PipelineBinaryCommands::hpexpire, key, millis, fields);
}
@@ -278,9 +278,9 @@ public List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condi
}
@Override
- public List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Condition condition, byte[]... fields) {
+ public List hExpireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition, byte[]... fields) {
- if (condition == FieldExpirationOptions.Condition.ALWAYS) {
+ if (condition == ExpirationOptions.Condition.ALWAYS) {
return connection.invoke().just(Jedis::hexpireAt, PipelineBinaryCommands::hexpireAt, key, unixTime, fields);
}
@@ -289,10 +289,10 @@ public List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Co
}
@Override
- public List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions.Condition condition,
+ public List hpExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition,
byte[]... fields) {
- if (condition == FieldExpirationOptions.Condition.ALWAYS) {
+ if (condition == ExpirationOptions.Condition.ALWAYS) {
return connection.invoke().just(Jedis::hpexpireAt, PipelineBinaryCommands::hpexpireAt, key, unixTimeInMillis,
fields);
}
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java
index 93f0ddfff6..9d4465b410 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java
@@ -15,6 +15,7 @@
*/
package org.springframework.data.redis.connection.jedis;
+import redis.clients.jedis.args.ExpiryOption;
import redis.clients.jedis.commands.JedisBinaryCommands;
import redis.clients.jedis.commands.PipelineBinaryCommands;
import redis.clients.jedis.params.RestoreParams;
@@ -30,6 +31,7 @@
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.redis.connection.DataType;
+import org.springframework.data.redis.connection.ExpirationOptions;
import org.springframework.data.redis.connection.RedisKeyCommands;
import org.springframework.data.redis.connection.SortParameters;
import org.springframework.data.redis.connection.ValueEncoding;
@@ -206,43 +208,69 @@ public Boolean renameNX(byte[] sourceKey, byte[] targetKey) {
}
@Override
- public Boolean expire(byte[] key, long seconds) {
+ public Boolean expire(byte[] key, long seconds, ExpirationOptions.Condition condition) {
Assert.notNull(key, "Key must not be null");
if (seconds > Integer.MAX_VALUE) {
- return pExpire(key, TimeUnit.SECONDS.toMillis(seconds));
+ return pExpire(key, TimeUnit.SECONDS.toMillis(seconds), condition);
}
- return connection.invoke().from(JedisBinaryCommands::expire, PipelineBinaryCommands::expire, key, seconds)
+ if (condition == ExpirationOptions.Condition.ALWAYS) {
+ return connection.invoke().from(JedisBinaryCommands::expire, PipelineBinaryCommands::expire, key, seconds)
+ .get(JedisConverters.longToBoolean());
+ }
+
+ ExpiryOption option = ExpiryOption.valueOf(condition.name());
+ return connection.invoke().from(JedisBinaryCommands::expire, PipelineBinaryCommands::expire, key, seconds, option)
.get(JedisConverters.longToBoolean());
}
@Override
- public Boolean pExpire(byte[] key, long millis) {
+ public Boolean pExpire(byte[] key, long millis, ExpirationOptions.Condition condition) {
Assert.notNull(key, "Key must not be null");
- return connection.invoke().from(JedisBinaryCommands::pexpire, PipelineBinaryCommands::pexpire, key, millis)
+ if (condition == ExpirationOptions.Condition.ALWAYS) {
+ return connection.invoke().from(JedisBinaryCommands::pexpire, PipelineBinaryCommands::pexpire, key, millis)
+ .get(JedisConverters.longToBoolean());
+ }
+
+ ExpiryOption option = ExpiryOption.valueOf(condition.name());
+ return connection.invoke().from(JedisBinaryCommands::pexpire, PipelineBinaryCommands::pexpire, key, millis, option)
.get(JedisConverters.longToBoolean());
}
@Override
- public Boolean expireAt(byte[] key, long unixTime) {
+ public Boolean expireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition) {
Assert.notNull(key, "Key must not be null");
- return connection.invoke().from(JedisBinaryCommands::expireAt, PipelineBinaryCommands::expireAt, key, unixTime)
+ if (condition == ExpirationOptions.Condition.ALWAYS) {
+ return connection.invoke().from(JedisBinaryCommands::expireAt, PipelineBinaryCommands::expireAt, key, unixTime)
+ .get(JedisConverters.longToBoolean());
+ }
+
+ ExpiryOption option = ExpiryOption.valueOf(condition.name());
+ return connection.invoke()
+ .from(JedisBinaryCommands::expireAt, PipelineBinaryCommands::expireAt, key, unixTime, option)
.get(JedisConverters.longToBoolean());
}
@Override
- public Boolean pExpireAt(byte[] key, long unixTimeInMillis) {
+ public Boolean pExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition) {
Assert.notNull(key, "Key must not be null");
+ if (condition == ExpirationOptions.Condition.ALWAYS) {
+ return connection.invoke()
+ .from(JedisBinaryCommands::pexpireAt, PipelineBinaryCommands::pexpireAt, key, unixTimeInMillis)
+ .get(JedisConverters.longToBoolean());
+ }
+
+ ExpiryOption option = ExpiryOption.valueOf(condition.name());
return connection.invoke()
- .from(JedisBinaryCommands::pexpireAt, PipelineBinaryCommands::pexpireAt, key, unixTimeInMillis)
+ .from(JedisBinaryCommands::pexpireAt, PipelineBinaryCommands::pexpireAt, key, unixTimeInMillis, option)
.get(JedisConverters.longToBoolean());
}
diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java
index 032d6230d6..278671704b 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java
@@ -29,7 +29,7 @@
import java.util.concurrent.TimeUnit;
import org.springframework.dao.InvalidDataAccessApiUsageException;
-import org.springframework.data.redis.connection.Hash.FieldExpirationOptions;
+import org.springframework.data.redis.connection.ExpirationOptions;
import org.springframework.data.redis.connection.RedisHashCommands;
import org.springframework.data.redis.connection.convert.Converters;
import org.springframework.data.redis.core.Cursor;
@@ -215,25 +215,25 @@ public Cursor> hScan(byte[] key, ScanOptions options) {
}
@Override
- public List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condition condition, byte[]... fields) {
+ public List hExpire(byte[] key, long seconds, ExpirationOptions.Condition condition, byte[]... fields) {
return connection.invoke().fromMany(RedisHashAsyncCommands::hexpire, key, seconds, getExpireArgs(condition), fields)
.toList();
}
@Override
- public List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condition condition, byte[]... fields) {
+ public List hpExpire(byte[] key, long millis, ExpirationOptions.Condition condition, byte[]... fields) {
return connection.invoke().fromMany(RedisHashAsyncCommands::hpexpire, key, millis, getExpireArgs(condition), fields)
.toList();
}
@Override
- public List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Condition condition, byte[]... fields) {
+ public List hExpireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition, byte[]... fields) {
return connection.invoke()
.fromMany(RedisHashAsyncCommands::hexpireat, key, unixTime, getExpireArgs(condition), fields).toList();
}
@Override
- public List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions.Condition condition,
+ public List hpExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition,
byte[]... fields) {
return connection.invoke()
.fromMany(RedisHashAsyncCommands::hpexpireat, key, unixTimeInMillis, getExpireArgs(condition), fields).toList();
@@ -314,13 +314,13 @@ private static Entry toEntry(KeyValue value) {
return value.hasValue() ? Converters.entryOf(value.getKey(), value.getValue()) : null;
}
- private ExpireArgs getExpireArgs(FieldExpirationOptions.Condition condition) {
+ private static ExpireArgs getExpireArgs(ExpirationOptions.Condition condition) {
return new ExpireArgs() {
@Override
public void build(CommandArgs args) {
- if (ObjectUtils.nullSafeEquals(condition, FieldExpirationOptions.Condition.ALWAYS)) {
+ if (ObjectUtils.nullSafeEquals(condition, ExpirationOptions.Condition.ALWAYS)) {
return;
}
diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceKeyCommands.java
index a9514cd793..78d4e7006e 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceKeyCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceKeyCommands.java
@@ -16,12 +16,14 @@
package org.springframework.data.redis.connection.lettuce;
import io.lettuce.core.CopyArgs;
+import io.lettuce.core.ExpireArgs;
import io.lettuce.core.KeyScanCursor;
import io.lettuce.core.RestoreArgs;
import io.lettuce.core.ScanArgs;
import io.lettuce.core.ScanCursor;
import io.lettuce.core.SortArgs;
import io.lettuce.core.api.async.RedisKeyAsyncCommands;
+import io.lettuce.core.protocol.CommandArgs;
import java.time.Duration;
import java.util.List;
@@ -30,6 +32,7 @@
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.redis.connection.DataType;
+import org.springframework.data.redis.connection.ExpirationOptions;
import org.springframework.data.redis.connection.RedisKeyCommands;
import org.springframework.data.redis.connection.SortParameters;
import org.springframework.data.redis.connection.ValueEncoding;
@@ -39,6 +42,7 @@
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
+import org.springframework.util.ObjectUtils;
/**
* @author Christoph Strobl
@@ -192,35 +196,35 @@ public Boolean renameNX(byte[] sourceKey, byte[] targetKey) {
}
@Override
- public Boolean expire(byte[] key, long seconds) {
+ public Boolean expire(byte[] key, long seconds, ExpirationOptions.Condition condition) {
Assert.notNull(key, "Key must not be null");
- return connection.invoke().just(RedisKeyAsyncCommands::expire, key, seconds);
+ return connection.invoke().just(RedisKeyAsyncCommands::expire, key, seconds, getExpireArgs(condition));
}
@Override
- public Boolean pExpire(byte[] key, long millis) {
+ public Boolean pExpire(byte[] key, long millis, ExpirationOptions.Condition condition) {
Assert.notNull(key, "Key must not be null");
- return connection.invoke().just(RedisKeyAsyncCommands::pexpire, key, millis);
+ return connection.invoke().just(RedisKeyAsyncCommands::pexpire, key, millis, getExpireArgs(condition));
}
@Override
- public Boolean expireAt(byte[] key, long unixTime) {
+ public Boolean expireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition) {
Assert.notNull(key, "Key must not be null");
- return connection.invoke().just(RedisKeyAsyncCommands::expireat, key, unixTime);
+ return connection.invoke().just(RedisKeyAsyncCommands::expireat, key, unixTime, getExpireArgs(condition));
}
@Override
- public Boolean pExpireAt(byte[] key, long unixTimeInMillis) {
+ public Boolean pExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition) {
Assert.notNull(key, "Key must not be null");
- return connection.invoke().just(RedisKeyAsyncCommands::pexpireat, key, unixTimeInMillis);
+ return connection.invoke().just(RedisKeyAsyncCommands::pexpireat, key, unixTimeInMillis, getExpireArgs(condition));
}
@Override
@@ -337,4 +341,19 @@ public Long refcount(byte[] key) {
return connection.invoke().just(RedisKeyAsyncCommands::objectRefcount, key);
}
+
+ private static ExpireArgs getExpireArgs(ExpirationOptions.Condition condition) {
+
+ return new ExpireArgs() {
+ @Override
+ public void build(CommandArgs args) {
+
+ if (ObjectUtils.nullSafeEquals(condition, ExpirationOptions.Condition.ALWAYS)) {
+ return;
+ }
+
+ args.add(condition.name());
+ }
+ };
+ }
}
diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java
index 84dd2ca906..e637296219 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java
@@ -31,7 +31,7 @@
import java.util.stream.Collectors;
import org.reactivestreams.Publisher;
-import org.springframework.data.redis.connection.Hash.FieldExpirationOptions;
+import org.springframework.data.redis.connection.ExpirationOptions;
import org.springframework.data.redis.connection.ReactiveHashCommands;
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
@@ -269,7 +269,8 @@ public Flux> hStrLen(Publisher> applyExpiration(Publisher commands) {
+ public Flux> applyHashFieldExpiration(
+ Publisher commands) {
return connection.execute(cmd -> Flux.from(commands).concatMap(command -> {
@@ -287,7 +288,7 @@ public Flux> applyExpiration(Publisher void build(CommandArgs args) {
super.build(args);
- if (ObjectUtils.nullSafeEquals(command.getOptions(), FieldExpirationOptions.none())) {
+ if (ObjectUtils.nullSafeEquals(command.getOptions(), ExpirationOptions.none())) {
return;
}
diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java
index a1371b7856..3dbb44c69d 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java
@@ -16,8 +16,10 @@
package org.springframework.data.redis.connection.lettuce;
import io.lettuce.core.CopyArgs;
+import io.lettuce.core.ExpireArgs;
import io.lettuce.core.ScanStream;
import io.lettuce.core.api.reactive.RedisKeyReactiveCommands;
+import io.lettuce.core.protocol.CommandArgs;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -25,9 +27,12 @@
import java.time.Duration;
import java.util.Collection;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import org.reactivestreams.Publisher;
+
import org.springframework.data.redis.connection.DataType;
+import org.springframework.data.redis.connection.ExpirationOptions;
import org.springframework.data.redis.connection.ReactiveKeyCommands;
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
@@ -38,6 +43,7 @@
import org.springframework.data.redis.connection.ValueEncoding.RedisValueEncoding;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.util.Assert;
+import org.springframework.util.ObjectUtils;
/**
* @author Christoph Strobl
@@ -206,27 +212,45 @@ public Flux, Long>> mUnlink(Publisher> expire(Publisher commands) {
+ public Flux> applyExpiration(Publisher commands) {
return connection.execute(cmd -> Flux.from(commands).concatMap(command -> {
Assert.notNull(command.getKey(), "Key must not be null");
- Assert.notNull(command.getTimeout(), "Timeout must not be null");
- return cmd.expire(command.getKey(), command.getTimeout().getSeconds())
- .map(value -> new BooleanResponse<>(command, value));
- }));
- }
+ if (command.getExpiration().isPersistent()) {
+ return cmd.persist(command.getKey()).map(value -> new BooleanResponse<>(command, value));
+ }
- @Override
- public Flux> pExpire(Publisher commands) {
+ ExpireArgs args = new ExpireArgs() {
- return connection.execute(cmd -> Flux.from(commands).concatMap(command -> {
+ @Override
+ public void build(CommandArgs args) {
+ super.build(args);
+ if (ObjectUtils.nullSafeEquals(command.getOptions(), ExpirationOptions.none())) {
+ return;
+ }
- Assert.notNull(command.getKey(), "Key must not be null");
- Assert.notNull(command.getTimeout(), "Timeout must not be null");
+ args.add(command.getOptions().getCondition().name());
+ }
+ };
+
+ if (command.getExpiration().isUnixTimestamp()) {
+
+ if (command.getExpiration().getTimeUnit().equals(TimeUnit.MILLISECONDS)) {
+ return cmd.pexpireat(command.getKey(), command.getExpiration().getExpirationTimeInMilliseconds(), args)
+ .map(value -> new BooleanResponse<>(command, value));
+ }
+ return cmd.expireat(command.getKey(), command.getExpiration().getExpirationTimeInSeconds(), args)
+ .map(value -> new BooleanResponse<>(command, value));
+ }
+
+ if (command.getExpiration().getTimeUnit().equals(TimeUnit.MILLISECONDS)) {
+ return cmd.pexpire(command.getKey(), command.getExpiration().getExpirationTimeInMilliseconds(), args)
+ .map(value -> new BooleanResponse<>(command, value));
+ }
- return cmd.pexpire(command.getKey(), command.getTimeout().toMillis())
+ return cmd.expire(command.getKey(), command.getExpiration().getExpirationTimeInSeconds(), args)
.map(value -> new BooleanResponse<>(command, value));
}));
}
@@ -239,7 +263,7 @@ public Flux> expireAt(Publisher new BooleanResponse<>(command, value));
}));
}
@@ -252,7 +276,7 @@ public Flux> pExpireAt(Publisher new BooleanResponse<>(command, value));
}));
}
@@ -319,4 +343,21 @@ public Mono idletime(ByteBuffer key) {
public Mono refcount(ByteBuffer key) {
return connection.execute(cmd -> cmd.objectRefcount(key)).next();
}
+
+ private static ExpireArgs getExpireArgs(ExpirationOptions options) {
+
+ return new ExpireArgs() {
+
+ @Override
+ public void build(CommandArgs args) {
+ super.build(args);
+ if (ObjectUtils.nullSafeEquals(options.getCondition(), ExpirationOptions.Condition.ALWAYS)) {
+ return;
+ }
+
+ args.add(options.getCondition().name());
+ }
+ };
+ }
+
}
diff --git a/src/main/java/org/springframework/data/redis/core/BoundHashFieldExpirationOperations.java b/src/main/java/org/springframework/data/redis/core/BoundHashFieldExpirationOperations.java
index d49041a66d..8546445af4 100644
--- a/src/main/java/org/springframework/data/redis/core/BoundHashFieldExpirationOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/BoundHashFieldExpirationOperations.java
@@ -19,7 +19,7 @@
import java.time.Instant;
import java.util.concurrent.TimeUnit;
-import org.springframework.data.redis.connection.Hash;
+import org.springframework.data.redis.connection.ExpirationOptions;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.core.types.Expirations;
import org.springframework.lang.Nullable;
@@ -40,18 +40,17 @@ public interface BoundHashFieldExpirationOperations {
* @return changes to the hash fields. {@literal null} when used in pipeline / transaction.
*/
default ExpireChanges expire(Expiration expiration) {
- return expire(expiration, Hash.FieldExpirationOptions.none());
+ return expire(expiration, ExpirationOptions.none());
}
/**
- * Apply {@link Expiration} to the bound hash key/hash fields given {@link Hash.FieldExpirationOptions expiration
- * options}.
+ * Apply {@link Expiration} to the bound hash key/hash fields given {@link ExpirationOptions expiration options}.
*
* @param expiration the expiration definition.
* @param options expiration options.
* @return changes to the hash fields. {@literal null} when used in pipeline / transaction.
*/
- ExpireChanges expire(Expiration expiration, Hash.FieldExpirationOptions options);
+ ExpireChanges expire(Expiration expiration, ExpirationOptions options);
/**
* Set time to live for the bound hash key/hash fields.
diff --git a/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java b/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java
index 0d287e929f..0355588ea4 100644
--- a/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java
@@ -223,7 +223,7 @@ public interface BoundHashOperations extends BoundKeyOperations {
* @return the bound operations object to perform operations on the hash field expiration.
* @since 3.5
*/
- default BoundHashFieldExpirationOperations expiration() {
+ default BoundHashFieldExpirationOperations hashExpiration() {
return new DefaultBoundHashFieldExpirationOperations<>(getOperations().opsForHash(), getKey(), this::keys);
}
@@ -235,8 +235,8 @@ default BoundHashFieldExpirationOperations expiration() {
* @return the bound operations object to perform operations on the hash field expiration.
* @since 3.5
*/
- default BoundHashFieldExpirationOperations expiration(HK... hashFields) {
- return expiration(Arrays.asList(hashFields));
+ default BoundHashFieldExpirationOperations hashExpiration(HK... hashFields) {
+ return hashExpiration(Arrays.asList(hashFields));
}
/**
@@ -247,7 +247,7 @@ default BoundHashFieldExpirationOperations expiration(HK... hashFields) {
* @return the bound operations object to perform operations on the hash field expiration.
* @since 3.5
*/
- default BoundHashFieldExpirationOperations expiration(Collection hashFields) {
+ default BoundHashFieldExpirationOperations hashExpiration(Collection hashFields) {
return new DefaultBoundHashFieldExpirationOperations<>(getOperations().opsForHash(), getKey(), () -> hashFields);
}
diff --git a/src/main/java/org/springframework/data/redis/core/BoundKeyExpirationOperations.java b/src/main/java/org/springframework/data/redis/core/BoundKeyExpirationOperations.java
new file mode 100644
index 0000000000..b50431b209
--- /dev/null
+++ b/src/main/java/org/springframework/data/redis/core/BoundKeyExpirationOperations.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.redis.core;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.concurrent.TimeUnit;
+
+import org.springframework.data.redis.connection.ExpirationOptions;
+import org.springframework.data.redis.core.types.Expiration;
+import org.springframework.data.redis.core.types.Expirations;
+import org.springframework.lang.Nullable;
+
+/**
+ * Key Expiration operations bound to a key.
+ *
+ * @author Mark Paluch
+ * @since 3.5
+ */
+public interface BoundKeyExpirationOperations {
+
+ /**
+ * Apply {@link Expiration} to the bound key without any additional constraints.
+ *
+ * @param expiration the expiration definition.
+ * @return changes to the key. {@literal null} when used in pipeline / transaction.
+ */
+ default ExpireChanges.ExpiryChangeState expire(Expiration expiration) {
+ return expire(expiration, ExpirationOptions.none());
+ }
+
+ /**
+ * Apply {@link Expiration} to the bound key given {@link ExpirationOptions expiration options}.
+ *
+ * @param expiration the expiration definition.
+ * @param options expiration options.
+ * @return changes to the key. {@literal null} when used in pipeline / transaction.
+ */
+ @Nullable
+ ExpireChanges.ExpiryChangeState expire(Expiration expiration, ExpirationOptions options);
+
+ /**
+ * Set time to live for the bound key.
+ *
+ * @param timeout the amount of time after which the key will be expired, must not be {@literal null}.
+ * @return changes to the key. {@literal null} when used in pipeline / transaction.
+ * @throws IllegalArgumentException if the timeout is {@literal null}.
+ * @see Redis Documentation: EXPIRE
+ * @since 3.5
+ */
+ @Nullable
+ ExpireChanges.ExpiryChangeState expire(Duration timeout);
+
+ /**
+ * Set the expiration for the bound key as a {@literal date} timestamp.
+ *
+ * @param expireAt must not be {@literal null}.
+ * @return changes to the key. {@literal null} when used in pipeline / transaction.
+ * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}.
+ * @see Redis Documentation: EXPIRE
+ * @since 3.5
+ */
+ @Nullable
+ ExpireChanges.ExpiryChangeState expireAt(Instant expireAt);
+
+ /**
+ * Remove the expiration from the bound key.
+ *
+ * @return changes to the key. {@literal null} when used in pipeline / transaction.
+ * @see Redis Documentation: PERSIST
+ * @since 3.5
+ */
+ @Nullable
+ ExpireChanges.ExpiryChangeState persist();
+
+ /**
+ * Get the time to live for the bound key in seconds.
+ *
+ * @return the actual expirations in seconds for the key. {@literal null} when used in pipeline / transaction.
+ * @see Redis Documentation: TTL
+ * @since 3.5
+ */
+ @Nullable
+ Expirations.TimeToLive getTimeToLive();
+
+ /**
+ * Get the time to live for the bound key and convert it to the given {@link TimeUnit}.
+ *
+ * @param timeUnit must not be {@literal null}.
+ * @return the actual expirations for the key in the given time unit. {@literal null} when used in pipeline /
+ * transaction.
+ * @see Redis Documentation: TTL
+ * @since 3.5
+ */
+ @Nullable
+ Expirations.TimeToLive getTimeToLive(TimeUnit timeUnit);
+
+}
diff --git a/src/main/java/org/springframework/data/redis/core/BoundKeyOperations.java b/src/main/java/org/springframework/data/redis/core/BoundKeyOperations.java
index e9d1f5e57c..bb8b438a75 100644
--- a/src/main/java/org/springframework/data/redis/core/BoundKeyOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/BoundKeyOperations.java
@@ -51,6 +51,16 @@ public interface BoundKeyOperations {
@Nullable
DataType getType();
+ /**
+ * Returns a bound operations object to perform expiration operations on the bound key.
+ *
+ * @return the bound operations object to perform operations on the hash field expiration.
+ * @since 3.5
+ */
+ default BoundKeyExpirationOperations expiration() {
+ return new DefaultBoundKeyExpirationOperations<>(getOperations(), getKey());
+ }
+
/**
* Returns the expiration of this key.
*
@@ -127,4 +137,10 @@ default Boolean expireAt(Instant expireAt) {
* @param newKey new key. Must not be {@literal null}.
*/
void rename(K newKey);
+
+ /**
+ * @return never {@literal null}.
+ */
+ RedisOperations getOperations();
+
}
diff --git a/src/main/java/org/springframework/data/redis/core/BoundOperationsProxyFactory.java b/src/main/java/org/springframework/data/redis/core/BoundOperationsProxyFactory.java
index 3492553b28..2c4bfc5f49 100644
--- a/src/main/java/org/springframework/data/redis/core/BoundOperationsProxyFactory.java
+++ b/src/main/java/org/springframework/data/redis/core/BoundOperationsProxyFactory.java
@@ -143,7 +143,7 @@ public Object invoke(MethodInvocation invocation) throws Throwable {
delegate.rename(invocation.getArguments()[0]);
yield null;
}
- case "getOperations" -> delegate.getOps();
+ case "getOperations" -> delegate.getOperations();
default ->
method.getDeclaringClass() == boundOperationsInterface ? doInvoke(invocation, method, operationsTarget, true)
: doInvoke(invocation, method, delegate, false);
@@ -234,12 +234,15 @@ public void rename(Object newKey) {
key = newKey;
}
+ @Override
public DataType getType() {
return type;
}
- public RedisOperations