Skip to content

Commit 5228bc7

Browse files
christophstroblmp911de
authored andcommitted
Add field expiration options to reactive API.
Make sure tests do not run when targeting older server versions. Introduce dedicated objects for time to live and status changes. See #3054
1 parent 50ea340 commit 5228bc7

31 files changed

+1859
-365
lines changed

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

+21
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.springframework.data.geo.Metric;
3131
import org.springframework.data.geo.Point;
3232
import org.springframework.data.redis.RedisSystemException;
33+
import org.springframework.data.redis.connection.Hash.FieldExpirationOptions;
3334
import org.springframework.data.redis.connection.convert.Converters;
3435
import org.springframework.data.redis.connection.convert.ListConverter;
3536
import org.springframework.data.redis.connection.convert.MapConverter;
@@ -2571,6 +2572,11 @@ public Long hStrLen(byte[] key, byte[] field) {
25712572
return convertAndReturn(delegate.hStrLen(key, field), Converters.identityConverter());
25722573
}
25732574

2575+
public @Nullable List<Long> expireHashField(byte[] key, org.springframework.data.redis.core.types.Expiration expiration,
2576+
FieldExpirationOptions options, byte[]... fields) {
2577+
return this.delegate.expireHashField(key, expiration, options, fields);
2578+
}
2579+
25742580
@Override
25752581
public List<Long> hExpire(byte[] key, long seconds, byte[]... fields) {
25762582
return this.delegate.hExpire(key, seconds, fields);
@@ -2601,11 +2607,21 @@ public List<Long> hTtl(byte[] key, byte[]... fields) {
26012607
return this.delegate.hTtl(key, fields);
26022608
}
26032609

2610+
@Override
2611+
public List<Long> hpTtl(byte[] key, byte[]... fields) {
2612+
return this.delegate.hpTtl(key, fields);
2613+
}
2614+
26042615
@Override
26052616
public List<Long> hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields) {
26062617
return this.delegate.hTtl(key, timeUnit, fields);
26072618
}
26082619

2620+
public @Nullable List<Long> expireHashField(String key, org.springframework.data.redis.core.types.Expiration expiration,
2621+
FieldExpirationOptions options, String... fields) {
2622+
return expireHashField(serialize(key), expiration, options, serializeMulti(fields));
2623+
}
2624+
26092625
@Override
26102626
public List<Long> hExpire(String key, long seconds, String... fields) {
26112627
return hExpire(serialize(key), seconds, serializeMulti(fields));
@@ -2641,6 +2657,11 @@ public List<Long> hTtl(String key, TimeUnit timeUnit, String... fields) {
26412657
return hTtl(serialize(key), timeUnit, serializeMulti(fields));
26422658
}
26432659

2660+
@Override
2661+
public List<Long> hpTtl(String key, String... fields) {
2662+
return hTtl(serialize(key), serializeMulti(fields));
2663+
}
2664+
26442665
@Override
26452666
public void setClientName(byte[] name) {
26462667
this.delegate.setClientName(name);

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

+16
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.data.geo.GeoResults;
2929
import org.springframework.data.geo.Metric;
3030
import org.springframework.data.geo.Point;
31+
import org.springframework.data.redis.connection.Hash.FieldExpirationOptions;
3132
import org.springframework.data.redis.connection.stream.ByteRecord;
3233
import org.springframework.data.redis.connection.stream.Consumer;
3334
import org.springframework.data.redis.connection.stream.MapRecord;
@@ -1527,6 +1528,21 @@ default List<Long> hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields) {
15271528
return hashCommands().hTtl(key, timeUnit, fields);
15281529
}
15291530

1531+
/** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */
1532+
@Override
1533+
@Deprecated
1534+
default List<Long> hpTtl(byte[] key, byte[]... fields) {
1535+
return hashCommands().hpTtl(key, fields);
1536+
}
1537+
1538+
/** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */
1539+
@Override
1540+
@Deprecated
1541+
default @Nullable List<Long> expireHashField(byte[] key, org.springframework.data.redis.core.types.Expiration expiration,
1542+
FieldExpirationOptions options, byte[]... fields) {
1543+
return hashCommands().expireHashField(key, expiration, options, fields);
1544+
}
1545+
15301546
// GEO COMMANDS
15311547

15321548
/** @deprecated in favor of {@link RedisConnection#geoCommands()}}. */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.redis.connection;
17+
18+
import java.util.Objects;
19+
20+
import org.springframework.lang.Contract;
21+
import org.springframework.lang.Nullable;
22+
import org.springframework.util.ObjectUtils;
23+
24+
/**
25+
* @author Christoph Strobl
26+
* @since 3.5
27+
*/
28+
public interface Hash {
29+
30+
class FieldExpirationOptions {
31+
32+
private static final FieldExpirationOptions NONE = new FieldExpirationOptions(null);
33+
private @Nullable Condition condition;
34+
35+
FieldExpirationOptions(@Nullable Condition condition) {
36+
this.condition = condition;
37+
}
38+
39+
public static FieldExpirationOptions none() {
40+
return NONE;
41+
}
42+
43+
@Contract("_ -> new")
44+
public static FieldExpireOptionsBuilder builder() {
45+
return new FieldExpireOptionsBuilder();
46+
}
47+
48+
public @Nullable Condition getCondition() {
49+
return condition;
50+
}
51+
52+
@Override
53+
public boolean equals(Object o) {
54+
if (o == this) {
55+
return true;
56+
}
57+
if (o == null || getClass() != o.getClass()) {
58+
return false;
59+
}
60+
FieldExpirationOptions that = (FieldExpirationOptions) o;
61+
return ObjectUtils.nullSafeEquals(this.condition, that.condition);
62+
}
63+
64+
@Override
65+
public int hashCode() {
66+
return Objects.hash(condition);
67+
}
68+
69+
public static class FieldExpireOptionsBuilder {
70+
71+
@Nullable Condition condition;
72+
73+
@Contract("_ -> this")
74+
public FieldExpireOptionsBuilder nx() {
75+
this.condition = Condition.NX;
76+
return this;
77+
}
78+
79+
@Contract("_ -> this")
80+
public FieldExpireOptionsBuilder xx() {
81+
this.condition = Condition.XX;
82+
return this;
83+
}
84+
85+
@Contract("_ -> this")
86+
public FieldExpireOptionsBuilder gt() {
87+
this.condition = Condition.GT;
88+
return this;
89+
}
90+
91+
@Contract("_ -> this")
92+
public FieldExpireOptionsBuilder lt() {
93+
this.condition = Condition.LT;
94+
return this;
95+
}
96+
97+
@Contract("_ -> !null")
98+
public FieldExpirationOptions build() {
99+
return condition == null ? NONE : new FieldExpirationOptions(condition);
100+
}
101+
}
102+
103+
public enum Condition {
104+
105+
/** Set expiration only when the field has no expiration. */
106+
NX,
107+
/** Set expiration only when the field has an existing expiration. */
108+
XX,
109+
/** Set expiration only when the new expiration is greater than current one. */
110+
GT,
111+
/** Set expiration only when the new expiration is greater than current one. */
112+
LT
113+
}
114+
}
115+
}

0 commit comments

Comments
 (0)