|
3 | 3 |
|
4 | 4 | This section contains details about migration steps, deprecations, and removals. |
5 | 5 |
|
| 6 | +[[upgrading.3-to-4]] |
| 7 | +== Upgrading from 3.x to 4.x |
| 8 | + |
| 9 | +Spring Data Redis 4.0 is part of the Spring Data 2025.1 release train and requires Spring Framework 7.0. |
| 10 | +Upgrade to the latest Spring Data Redis 3.5.x release and replace APIs deprecated for removal before moving to 4.x. |
| 11 | + |
| 12 | +Spring Data 2025.1 retains the Java 17 baseline and upgrades to Jakarta EE 11 and Kotlin 2.2. |
| 13 | +Review the https://github.com/spring-projects/spring-data-commons/wiki/Spring-Data-2025.1-Release-Notes[Spring Data 2025.1 release notes] and the https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-7.0-Release-Notes[Spring Framework 7.0 release notes] for baseline changes. |
| 14 | +See the xref:commons/upgrade.adoc[Spring Data Commons upgrade documentation] for changes shared by all Spring Data modules. |
| 15 | + |
| 16 | +[[upgrading.3-to-4.nullability]] |
| 17 | +=== JSpecify Nullability |
| 18 | + |
| 19 | +Spring Data Redis 4.0 uses https://jspecify.dev/[JSpecify] nullability annotations and null-marks its packages by default. |
| 20 | +Spring's `org.springframework.lang` nullability annotations are deprecated in Spring Framework 7.0 in favor of JSpecify. |
| 21 | +JSpecify can describe the nullability of generic type arguments and array elements. |
| 22 | +Java nullness tools and Kotlin can report additional errors or warnings. |
| 23 | +See the Spring Framework documentation on https://docs.spring.io/spring-framework/reference/core/null-safety.html[null-safety] for details. |
| 24 | + |
| 25 | +Recompile custom implementations and Kotlin code against Spring Data Redis 4.0. |
| 26 | +Review any new nullability errors and warnings. |
| 27 | +In particular, `RedisSerializer.serialize(Object)` must now return a non-null byte array. |
| 28 | +Custom serializers that previously returned `null` must return a non-null representation, such as an empty byte array for a `null` value. |
| 29 | +`RedisSerializer.deserialize(byte[])` can still return `null`. |
| 30 | + |
| 31 | +[[upgrading.3-to-4.jackson]] |
| 32 | +=== Jackson 3 |
| 33 | + |
| 34 | +Spring Data Redis 4.0 adds Jackson 3 serializers and a Jackson 3 hash mapper. |
| 35 | +Jackson 3 implementation types are in the `tools.jackson` package. |
| 36 | +The separate `jackson-annotations` artifact remains in `com.fasterxml.jackson.annotation`. |
| 37 | +See the https://github.com/FasterXML/jackson/blob/main/jackson3/MIGRATING_TO_JACKSON_3.md[Jackson 3 migration guide] for Jackson-wide changes. |
| 38 | +Jackson 3 serializers require `tools.jackson.core:jackson-databind` on the classpath. |
| 39 | +Jackson 2 and Jackson 3 can coexist on the classpath because their implementation packages differ. |
| 40 | + |
| 41 | +The deprecated Jackson 2 types remain available during migration: |
| 42 | + |
| 43 | +|=== |
| 44 | +|Purpose |Jackson 3 API |Jackson 2 API (deprecated) |
| 45 | + |
| 46 | +|Generic JSON serializer |
| 47 | +|`GenericJacksonJsonRedisSerializer` |
| 48 | +|`GenericJackson2JsonRedisSerializer` |
| 49 | + |
| 50 | +|Typed JSON serializer |
| 51 | +|`JacksonJsonRedisSerializer` |
| 52 | +|`Jackson2JsonRedisSerializer` |
| 53 | + |
| 54 | +|Hash mapper |
| 55 | +|`JacksonHashMapper` |
| 56 | +|`Jackson2HashMapper` |
| 57 | + |
| 58 | +|Custom reader and writer contracts |
| 59 | +|`JacksonObjectReader` and `JacksonObjectWriter` |
| 60 | +|`Jackson2ObjectReader` and `Jackson2ObjectWriter` |
| 61 | + |
| 62 | +|=== |
| 63 | + |
| 64 | +In 3.x, `JacksonObjectReader` and `JacksonObjectWriter` were Jackson 2 contracts. |
| 65 | +In 4.x, the same names are Jackson 3 contracts. |
| 66 | +Code that still uses Jackson 2 must use `Jackson2ObjectReader` and `Jackson2ObjectWriter`. |
| 67 | +This also applies to `GenericJackson2JsonRedisSerializer` and `Jackson2JsonRedisSerializer` constructor arguments and to the generic serializer builder's `reader(...)` and `writer(...)` methods. |
| 68 | + |
| 69 | +`RedisSerializer.json()` now returns a `GenericJacksonJsonRedisSerializer` instead of a `GenericJackson2JsonRedisSerializer`. |
| 70 | +Jackson 2 and Jackson 3 can produce different JSON. |
| 71 | +Applications that must read data written by Spring Data Redis 3.x can keep the Jackson 2 serializer until the stored data is migrated: |
| 72 | + |
| 73 | +[source,java] |
| 74 | +---- |
| 75 | +RedisSerializer<Object> serializer = new GenericJackson2JsonRedisSerializer(); |
| 76 | +---- |
| 77 | + |
| 78 | +`JacksonHashMapper` has a compatibility mode that reads hashes written by `Jackson2HashMapper` and writes hashes that `Jackson2HashMapper` can read: |
| 79 | + |
| 80 | +[source,java] |
| 81 | +---- |
| 82 | +JacksonHashMapper mapper = JacksonHashMapper.builder() |
| 83 | + .jackson2CompatibilityMode() |
| 84 | + .build(); |
| 85 | +---- |
| 86 | + |
| 87 | +Select `flatten()` or `hierarchical()` on the builder to match the existing hash representation. |
| 88 | +The Jackson 2 serializers and hash mapper are deprecated for removal and should only be used during migration. |
| 89 | + |
| 90 | +[[upgrading.3-to-4.cache]] |
| 91 | +=== Redis Cache |
| 92 | + |
| 93 | +`RedisCacheWriter` now uses the Spring Cache API method names for eviction. |
| 94 | +`remove(String, byte[])` and `clean(String, byte[])` are deprecated in favor of `evict(String, byte[])` and `clear(String, byte[])`. |
| 95 | +The old methods remain as default methods, but `evict` and `clear` are new abstract methods. |
| 96 | +Custom `RedisCacheWriter` implementations must implement the new methods. |
| 97 | + |
| 98 | +If the configured `RedisConnectionFactory` also implements `ReactiveRedisConnectionFactory`, the default `RedisCacheWriter` can perform writes asynchronously. |
| 99 | +`LettuceConnectionFactory` implements both interfaces. |
| 100 | +A write, eviction, or clear operation may finish after the calling method returns. |
| 101 | +Use `Cache.evictIfPresent(Object)` or `Cache.invalidate()` when an individual operation requires immediate visibility. |
| 102 | +To require immediate visibility for all cache writes, create the writer with `immediateWrites()`: |
| 103 | + |
| 104 | +[source,java] |
| 105 | +---- |
| 106 | +RedisCacheWriter cacheWriter = RedisCacheWriter.create(connectionFactory, |
| 107 | + configurer -> configurer.immediateWrites()); |
| 108 | +---- |
| 109 | + |
| 110 | +With a reactive connection factory, immediate writes use blocking access. |
| 111 | + |
| 112 | +.Cache API Changes |
| 113 | +|=== |
| 114 | +|3.x API |4.x replacement |
| 115 | + |
| 116 | +|`RedisCacheConfiguration.getTtl()` |
| 117 | +|`RedisCacheConfiguration.getTtlFunction()`. Evaluate the function with the cache key and value when a `Duration` is required. |
| 118 | + |
| 119 | +|`RedisCacheManager(RedisCacheWriter, RedisCacheConfiguration, Map, boolean)` |
| 120 | +|`RedisCacheManager(RedisCacheWriter, RedisCacheConfiguration, boolean, Map)` |
| 121 | + |
| 122 | +|`RedisCacheWriter.remove(String, byte[])` |
| 123 | +|`RedisCacheWriter.evict(String, byte[])` |
| 124 | + |
| 125 | +|`RedisCacheWriter.clean(String, byte[])` |
| 126 | +|`RedisCacheWriter.clear(String, byte[])` |
| 127 | + |
| 128 | +|=== |
| 129 | + |
| 130 | +[[upgrading.3-to-4.string-redis-template]] |
| 131 | +=== StringRedisTemplate Connection Callbacks |
| 132 | + |
| 133 | +`StringRedisTemplate` no longer wraps the `RedisConnection` supplied to `RedisCallback` code in a `DefaultStringRedisConnection`. |
| 134 | +Remove casts from the supplied connection to `StringRedisConnection`. |
| 135 | +Use the binary `RedisConnection` API directly or wrap the connection explicitly: |
| 136 | + |
| 137 | +[source,java] |
| 138 | +---- |
| 139 | +String value = stringRedisTemplate.execute(connection -> { |
| 140 | + StringRedisConnection strings = new DefaultStringRedisConnection(connection); |
| 141 | + return strings.get("key"); |
| 142 | +}); |
| 143 | +---- |
| 144 | + |
| 145 | +The template still uses string serializers for keys and values. |
| 146 | +Only direct connection access inside callbacks changes. |
| 147 | + |
| 148 | +[[upgrading.3-to-4.lettuce-observability]] |
| 149 | +=== Lettuce Observability |
| 150 | + |
| 151 | +Spring Data Redis 4.0 removes the deprecated observability adapter. |
| 152 | +The removed public types are `MicrometerTracingAdapter`, `LettuceObservationContext`, and `RedisObservation` from `org.springframework.data.redis.connection.lettuce.observability`. |
| 153 | + |
| 154 | +Replace `MicrometerTracingAdapter` with Lettuce's `io.lettuce.core.tracing.MicrometerTracing`: |
| 155 | + |
| 156 | +[source,java] |
| 157 | +---- |
| 158 | +MicrometerTracing tracing = new MicrometerTracing(observationRegistry, "Redis"); |
| 159 | +
|
| 160 | +ClientResources clientResources = ClientResources.builder() |
| 161 | + .tracing(tracing) |
| 162 | + .build(); |
| 163 | +---- |
| 164 | + |
| 165 | +Configure these `ClientResources` on `LettuceClientConfiguration`. |
| 166 | +See the Lettuce https://redis.github.io/lettuce/advanced-usage/observability/#micrometer-tracing[Micrometer Tracing documentation] for configuration details. |
| 167 | + |
| 168 | +[[upgrading.3-to-4.drivers]] |
| 169 | +=== Driver and Dependency Upgrades |
| 170 | + |
| 171 | +Spring Data Redis 4.0 updates its driver and networking baselines. |
| 172 | +Patch versions vary by maintenance release. |
| 173 | +Use the versions managed by the Spring Data BOM. |
| 174 | + |
| 175 | +|=== |
| 176 | +|Dependency |3.5 baseline |4.0 baseline |Migration notes |
| 177 | + |
| 178 | +|Jedis |
| 179 | +|6.x |
| 180 | +|7.x |
| 181 | +|Review the https://redis.github.io/jedis/migration-guides/v6-to-v7/[Jedis 7 migration guide], especially if application code uses native Jedis APIs. |
| 182 | + |
| 183 | +|Lettuce |
| 184 | +|6.6.x |
| 185 | +|6.8.x |
| 186 | +|Review the Lettuce https://redis.github.io/lettuce/new-features/#whats-new-in-lettuce-68[6.8 release notes] if application code uses native Lettuce APIs. |
| 187 | + |
| 188 | +|Netty, through Lettuce |
| 189 | +|4.1.x |
| 190 | +|4.2.x |
| 191 | +|Align explicitly managed Netty modules and native transports with the managed version. |
| 192 | + |
| 193 | +|=== |
| 194 | + |
| 195 | +[[upgrading.3-to-4.changed-api]] |
| 196 | +=== Other Removed and Changed APIs |
| 197 | + |
| 198 | +Most removed APIs were deprecated in 3.x. |
| 199 | +Replace them on the latest 3.5.x release before upgrading to 4.x. |
| 200 | + |
| 201 | +.Configuration, Operations, and Utilities |
| 202 | +|=== |
| 203 | +|3.x API |4.x replacement or action |
| 204 | + |
| 205 | +|`new RedisClusterConfiguration(PropertySource)` |
| 206 | +|`RedisClusterConfiguration.of(PropertySource)` |
| 207 | + |
| 208 | +|`new RedisSentinelConfiguration(PropertySource)` |
| 209 | +|`RedisSentinelConfiguration.of(PropertySource)` |
| 210 | + |
| 211 | +|`BoundSetOperations.diff(...)` |
| 212 | +|`BoundSetOperations.difference(...)` |
| 213 | + |
| 214 | +|`BoundZSetOperations`, `ZSetOperations`, and `RedisZSet` overloads accepting `RedisZSetCommands.Range` |
| 215 | +|Use the overloads accepting `org.springframework.data.domain.Range<String>`. |
| 216 | + |
| 217 | +|`RedisPersistentEntity.hasExplictTimeToLiveProperty()` |
| 218 | +|`RedisPersistentEntity.hasExplicitTimeToLiveProperty()` |
| 219 | + |
| 220 | +|`Converters.toBoolean(Long)` returning `Boolean` |
| 221 | +|The method now returns primitive `boolean`. |
| 222 | + |
| 223 | +|`Converters.toTimeMillis(...)` returning `Long` |
| 224 | +|The methods now return primitive `long`. |
| 225 | + |
| 226 | +|`LettuceConverters.toBytesList(Collection<byte[]>)` |
| 227 | +|Use the collection directly or create a `List` from it. |
| 228 | + |
| 229 | +|`ByteUtils.extractBytes(ByteBuffer)` |
| 230 | +|`ByteUtils.getBytes(ByteBuffer)` |
| 231 | + |
| 232 | +|`RedisAssertions` |
| 233 | +|Use `org.springframework.util.Assert`, `java.util.Objects`, or an explicit check and exception. |
| 234 | + |
| 235 | +|`DecodeUtils` |
| 236 | +|Use `java.util.Base64` directly. |
| 237 | + |
| 238 | +|=== |
| 239 | + |
| 240 | +.Extension APIs |
| 241 | +|=== |
| 242 | +|3.x API |4.x replacement or action |
| 243 | + |
| 244 | +|`org.springframework.data.util.TypeInformation` |
| 245 | +|`org.springframework.data.core.TypeInformation`. This package change affects Redis mapping extension points including `IndexResolver`, `IndexDefinition.IndexingContext`, `BasicRedisPersistentEntity`, and `RedisMappingContext`. |
| 246 | + |
| 247 | +|`new ScanIteration(long, Collection)` and `ScanIteration.getCursorId()` |
| 248 | +|Use `new ScanIteration(Cursor.CursorId, Collection)` and `ScanIteration.getId()`. |
| 249 | + |
| 250 | +|`ScanCursor.doOpen(long)` and `ScanCursor.isFinished(long)` |
| 251 | +|Override the variants accepting `Cursor.CursorId`. |
| 252 | + |
| 253 | +|`RedisPartTreeQuery` constructor accepting `QueryMethodEvaluationContextProvider` |
| 254 | +|Use the constructor accepting `ValueExpressionDelegate`. |
| 255 | + |
| 256 | +|`JedisClusterTopologyProvider.shouldUseCachedValue()` |
| 257 | +|Override `shouldUseCachedValue(JedisClusterTopology)`. |
| 258 | + |
| 259 | +|=== |
| 260 | + |
6 | 261 | [[upgrading.2-to-3]] |
7 | 262 | == Upgrading from 2.x to 3.x |
8 | 263 |
|
@@ -264,5 +519,3 @@ This effects methods on `LettuceConnectionFactory` and `LettuceConnection`. |
264 | 519 |
|
265 | 520 | `AuthenticatingRedisClient` has been removed without replacement. |
266 | 521 | Please refer to the https://lettuce.io/core/release/reference/index.html#basic.redisuri[driver documentation] for `RedisURI` to set authentication data. |
267 | | - |
268 | | - |
|
0 commit comments