diff --git a/README.md b/README.md
index 61570b427..9445efd85 100644
--- a/README.md
+++ b/README.md
@@ -87,34 +87,18 @@ Redis OM Spring provides all Spring Data Redis capabilities, plus:
- `@Vectorize` annotation to generate embeddings for text and images for use in Vector Similarity Searches
- Vector Similarity Search API (See [Redis Stack Vectors](https://redis.io/docs/stack/search/reference/vectors/))
-### 📋 Version Requirements
-
-Redis OM Spring has the following version requirements:
-
-| Dependency | Minimum Version | Recommended Version | Notes |
-|------------|----------------|-------------------|--------|
-| **Spring Boot** | 3.3.x | 3.4.x or 3.5.x | Built with Spring Boot 3.4.5 |
-| **Spring Data Redis** | 3.4.1 | 3.4.5 or later | Aligned with Spring Boot version |
-| **Spring Framework** | 6.2.x | Latest 6.x | Transitive via Spring Boot |
-| **Jedis** | 5.2.0 | 5.2.0 or later | Redis Java client |
-| **Java** | 17 | 17 or 21 | Spring Boot 3.x requires Java 17+ |
-| **Redis Stack** | 6.2.x | 7.2.x or later | For JSON and Search modules |
-
-#### Spring Boot Version Compatibility Policy
-
-Redis OM Spring follows an **N-2 support policy** for Spring Boot versions:
-
-- We build with the latest stable Spring Boot version
-- We support the current version and two previous minor versions that are still receiving OSS updates
-- We upgrade Spring Boot with each Redis OM Spring release
-
-For example, as of Redis OM Spring 1.0.0-RC4:
-
-- **Built with**: Spring Boot 3.4.5
-- **Minimum supported**: Spring Boot 3.3.x
-- **Recommended**: Spring Boot 3.4.x or 3.5.x
-
-⚠️ **Note**: Using older Spring Boot versions may work but is not officially tested or supported. For production use, we recommend staying within the supported version range.
+> [!IMPORTANT]
+> ### 📋 Version Compatibility
+>
+> Choose the correct Redis OM Spring version for your Spring Boot version:
+>
+> | Redis OM Spring | Spring Boot | Java | Status |
+> |-----------------|-------------|------|--------|
+> | **1.0.x** | 3.4.x | 17+ | Maintenance |
+> | **1.1.x** | 3.5.x | 17+ | Current Stable |
+> | **2.0.x** | 4.0.x | 17+ | Latest |
+>
+> **Always use the Redis OM Spring version that matches your Spring Boot version.**
## 🏁 Getting Started
@@ -492,7 +476,7 @@ This will unlock powerful AI-driven features for your applications, making data
For Maven, things normally just work, when you run `./mvnw spring-boot:run`. Some users have experienced this not being
the case, in which I recommend to explicitly declaring the `maven-compiler-plugin` in the case below it is paired with
-an app created with [`start.spring.io`](https://start.spring.io/) with Spring Boot `v3.3.0` (all other versions can be
+an app created with [`start.spring.io`](https://start.spring.io/) with Spring Boot `v4.0.0` (all other versions can be
inherited from the parent poms):
```xml
@@ -505,7 +489,7 @@ inherited from the parent poms):
org.springframework.bootspring-boot-configuration-processor
- 3.3.0
+ 4.0.0org.projectlombok
@@ -515,7 +499,7 @@ inherited from the parent poms):
com.redis.omredis-om-spring
- 1.0.0-RC.1
+ 2.0.0
diff --git a/demos/build.gradle b/demos/build.gradle
index 8f7732fb5..3f068d47a 100644
--- a/demos/build.gradle
+++ b/demos/build.gradle
@@ -71,7 +71,7 @@ dependencies {
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.mockito:mockito-core'
testImplementation "com.redis:testcontainers-redis:${testcontainersRedisVersion}"
- testImplementation "org.testcontainers:junit-jupiter"
+ testImplementation "org.testcontainers:junit-jupiter:1.20.4"
// Optional dependencies used by some demos
implementation 'com.github.javafaker:javafaker:1.0.2'
diff --git a/demos/roms-amr-entraid/build.gradle b/demos/roms-amr-entraid/build.gradle
index f8d5c64e8..6d8c79553 100644
--- a/demos/roms-amr-entraid/build.gradle
+++ b/demos/roms-amr-entraid/build.gradle
@@ -52,6 +52,8 @@ dependencies {
// Test dependencies
testImplementation 'org.springframework.boot:spring-boot-starter-test'
+ testImplementation "com.redis:testcontainers-redis:${testcontainersRedisVersion}"
+ testImplementation "org.testcontainers:junit-jupiter:1.20.4"
}
// Use -parameters flag for Spring
diff --git a/demos/roms-documents/build.gradle b/demos/roms-documents/build.gradle
index 21df7207d..9df1a6bca 100644
--- a/demos/roms-documents/build.gradle
+++ b/demos/roms-documents/build.gradle
@@ -47,8 +47,9 @@ dependencies {
// Test dependencies
testImplementation 'org.springframework.boot:spring-boot-starter-test'
+ testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
testImplementation "com.redis:testcontainers-redis:${testcontainersRedisVersion}"
- testImplementation "org.testcontainers:junit-jupiter"
+ testImplementation "org.testcontainers:junit-jupiter:1.20.4"
}
// Use -parameters flag for Spring
diff --git a/demos/roms-documents/src/test/java/com/redis/om/documents/controllers/EventControllerTest.java b/demos/roms-documents/src/test/java/com/redis/om/documents/controllers/EventControllerTest.java
index fbd574c94..8e16b20cd 100644
--- a/demos/roms-documents/src/test/java/com/redis/om/documents/controllers/EventControllerTest.java
+++ b/demos/roms-documents/src/test/java/com/redis/om/documents/controllers/EventControllerTest.java
@@ -13,7 +13,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
diff --git a/demos/roms-hashes/build.gradle b/demos/roms-hashes/build.gradle
index 58d5fdda0..08779d220 100644
--- a/demos/roms-hashes/build.gradle
+++ b/demos/roms-hashes/build.gradle
@@ -47,6 +47,8 @@ dependencies {
// Test dependencies
testImplementation 'org.springframework.boot:spring-boot-starter-test'
+ testImplementation "com.redis:testcontainers-redis:${testcontainersRedisVersion}"
+ testImplementation "org.testcontainers:junit-jupiter:1.20.4"
}
// Use -parameters flag for Spring
diff --git a/demos/roms-modeling/build.gradle b/demos/roms-modeling/build.gradle
index 01f664d09..577b71ccb 100644
--- a/demos/roms-modeling/build.gradle
+++ b/demos/roms-modeling/build.gradle
@@ -53,8 +53,10 @@ dependencies {
// Test dependencies
testImplementation 'org.springframework.boot:spring-boot-starter-test'
+ testImplementation 'org.springframework.boot:spring-boot-resttestclient'
+ testImplementation 'org.springframework.boot:spring-boot-data-redis'
testImplementation "com.redis:testcontainers-redis:${testcontainersRedisVersion}"
- testImplementation "org.testcontainers:junit-jupiter"
+ testImplementation "org.testcontainers:junit-jupiter:1.20.4"
testImplementation 'org.mockito:mockito-core'
}
diff --git a/demos/roms-modeling/src/test/java/com/foogaro/modeling/config/TestRedisConfiguration.java b/demos/roms-modeling/src/test/java/com/foogaro/modeling/config/TestRedisConfiguration.java
index 7418721c7..dab591099 100644
--- a/demos/roms-modeling/src/test/java/com/foogaro/modeling/config/TestRedisConfiguration.java
+++ b/demos/roms-modeling/src/test/java/com/foogaro/modeling/config/TestRedisConfiguration.java
@@ -4,7 +4,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
-import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.boot.data.redis.autoconfigure.DataRedisAutoConfiguration;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
@@ -24,7 +24,7 @@
@TestConfiguration
@AutoConfigureAfter(
- RedisAutoConfiguration.class
+ DataRedisAutoConfiguration.class
)
@Testcontainers
@Disabled(
diff --git a/demos/roms-modeling/src/test/java/com/foogaro/modeling/controller/TestTextDataController.java b/demos/roms-modeling/src/test/java/com/foogaro/modeling/controller/TestTextDataController.java
index f9969d86f..c4543473b 100644
--- a/demos/roms-modeling/src/test/java/com/foogaro/modeling/controller/TestTextDataController.java
+++ b/demos/roms-modeling/src/test/java/com/foogaro/modeling/controller/TestTextDataController.java
@@ -13,7 +13,7 @@
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.boot.resttestclient.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
diff --git a/demos/roms-multi-acl-account/build.gradle b/demos/roms-multi-acl-account/build.gradle
index 1f4ca8102..abbda3ba2 100644
--- a/demos/roms-multi-acl-account/build.gradle
+++ b/demos/roms-multi-acl-account/build.gradle
@@ -31,6 +31,10 @@ repositories {
dependencies {
implementation project(':redis-om-spring')
+ // Spring Boot Data Redis (for auto-configuration classes in Spring Boot 4.0)
+ implementation 'org.springframework.boot:spring-boot-starter-data-redis'
+ implementation 'org.springframework.boot:spring-boot-data-redis'
+
// Important for RedisOM annotation processing!
annotationProcessor project(':redis-om-spring')
testAnnotationProcessor project(':redis-om-spring')
@@ -44,7 +48,7 @@ dependencies {
// Test dependencies
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation "com.redis:testcontainers-redis:${testcontainersRedisVersion}"
- testImplementation "org.testcontainers:junit-jupiter"
+ testImplementation "org.testcontainers:junit-jupiter:1.20.4"
}
// Use -parameters flag for Spring
diff --git a/demos/roms-multi-acl-account/src/main/java/com/redis/romsmultiaclaccount/RomsMultiAclAccountApplication.java b/demos/roms-multi-acl-account/src/main/java/com/redis/romsmultiaclaccount/RomsMultiAclAccountApplication.java
index 2cb03abae..eb946e0f3 100644
--- a/demos/roms-multi-acl-account/src/main/java/com/redis/romsmultiaclaccount/RomsMultiAclAccountApplication.java
+++ b/demos/roms-multi-acl-account/src/main/java/com/redis/romsmultiaclaccount/RomsMultiAclAccountApplication.java
@@ -2,8 +2,11 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.data.redis.autoconfigure.DataRedisRepositoriesAutoConfiguration;
-@SpringBootApplication
+@SpringBootApplication(
+ exclude = DataRedisRepositoriesAutoConfiguration.class
+)
public class RomsMultiAclAccountApplication {
public static void main(String[] args) {
diff --git a/demos/roms-multi-acl-account/src/main/java/com/redis/romsmultiaclaccount/config/RedisConnectionFactoryConfig.java b/demos/roms-multi-acl-account/src/main/java/com/redis/romsmultiaclaccount/config/RedisConnectionFactoryConfig.java
index d904b0a36..3c54377a0 100644
--- a/demos/roms-multi-acl-account/src/main/java/com/redis/romsmultiaclaccount/config/RedisConnectionFactoryConfig.java
+++ b/demos/roms-multi-acl-account/src/main/java/com/redis/romsmultiaclaccount/config/RedisConnectionFactoryConfig.java
@@ -6,9 +6,9 @@
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnThreading;
-import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
-import org.springframework.boot.autoconfigure.thread.Threading;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.data.redis.autoconfigure.DataRedisProperties;
+import org.springframework.boot.thread.Threading;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@@ -32,15 +32,15 @@
@Configuration
@EnableConfigurationProperties(
- { RedisProperties.class }
+ { DataRedisProperties.class }
)
public class RedisConnectionFactoryConfig {
private static final Log logger = LogFactory.getLog(RedisConnectionFactoryConfig.class);
- private final RedisProperties redisProperties;
+ private final DataRedisProperties redisProperties;
- public RedisConnectionFactoryConfig(RedisProperties redisProperties) {
+ public RedisConnectionFactoryConfig(DataRedisProperties redisProperties) {
this.redisProperties = redisProperties;
}
diff --git a/demos/roms-multitenant/build.gradle b/demos/roms-multitenant/build.gradle
index d3727f79a..b28ec14ae 100644
--- a/demos/roms-multitenant/build.gradle
+++ b/demos/roms-multitenant/build.gradle
@@ -48,7 +48,7 @@ dependencies {
// Test dependencies
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation "com.redis:testcontainers-redis:${testcontainersRedisVersion}"
- testImplementation "org.testcontainers:junit-jupiter"
+ testImplementation "org.testcontainers:junit-jupiter:1.20.4"
}
// Use -parameters flag for Spring
diff --git a/demos/roms-multitenant/src/test/java/com/redis/om/multitenant/RomsMultitenantApplicationTests.java b/demos/roms-multitenant/src/test/java/com/redis/om/multitenant/RomsMultitenantApplicationTests.java
index ff27468cc..846657f2b 100644
--- a/demos/roms-multitenant/src/test/java/com/redis/om/multitenant/RomsMultitenantApplicationTests.java
+++ b/demos/roms-multitenant/src/test/java/com/redis/om/multitenant/RomsMultitenantApplicationTests.java
@@ -21,10 +21,19 @@
import com.redis.om.spring.indexing.RediSearchIndexer;
import com.redis.testcontainers.RedisStackContainer;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Configuration;
+
@Testcontainers
-@SpringBootTest
+@SpringBootTest(classes = RomsMultitenantApplicationTests.Config.class,
+ properties = { "spring.main.allow-bean-definition-overriding=true" })
class RomsMultitenantApplicationTests {
+ @SpringBootApplication
+ @Configuration
+ static class Config extends TestConfig {
+ }
+
@Container
static RedisStackContainer redis = new RedisStackContainer(
RedisStackContainer.DEFAULT_IMAGE_NAME.withTag(RedisStackContainer.DEFAULT_TAG));
diff --git a/demos/roms-multitenant/src/test/java/com/redis/om/multitenant/TestConfig.java b/demos/roms-multitenant/src/test/java/com/redis/om/multitenant/TestConfig.java
new file mode 100644
index 000000000..cd0bab725
--- /dev/null
+++ b/demos/roms-multitenant/src/test/java/com/redis/om/multitenant/TestConfig.java
@@ -0,0 +1,48 @@
+package com.redis.om.multitenant;
+
+import java.time.Duration;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.core.env.Environment;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
+import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
+import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
+import org.springframework.data.redis.core.StringRedisTemplate;
+
+import redis.clients.jedis.JedisPoolConfig;
+
+public class TestConfig {
+ @Autowired
+ Environment env;
+
+ @Bean
+ public JedisConnectionFactory jedisConnectionFactory() {
+ String host = env.getProperty("spring.data.redis.host", "localhost");
+ int port = env.getProperty("spring.data.redis.port", Integer.class, 6379);
+
+ RedisStandaloneConfiguration conf = new RedisStandaloneConfiguration(host, port);
+
+ final JedisPoolConfig poolConfig = new JedisPoolConfig();
+ poolConfig.setTestWhileIdle(false);
+ poolConfig.setMinEvictableIdleDuration(Duration.ofMillis(60000));
+ poolConfig.setTimeBetweenEvictionRuns(Duration.ofMillis(30000));
+ poolConfig.setNumTestsPerEvictionRun(-1);
+
+ final int timeout = 10000;
+
+ final JedisClientConfiguration jedisClientConfiguration = JedisClientConfiguration.builder().connectTimeout(Duration
+ .ofMillis(timeout)).readTimeout(Duration.ofMillis(timeout)).usePooling().poolConfig(poolConfig).build();
+
+ return new JedisConnectionFactory(conf, jedisClientConfiguration);
+ }
+
+ @Bean
+ public StringRedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
+ StringRedisTemplate template = new StringRedisTemplate();
+ template.setConnectionFactory(connectionFactory);
+
+ return template;
+ }
+}
diff --git a/demos/roms-permits/build.gradle b/demos/roms-permits/build.gradle
index 58d5fdda0..08779d220 100644
--- a/demos/roms-permits/build.gradle
+++ b/demos/roms-permits/build.gradle
@@ -47,6 +47,8 @@ dependencies {
// Test dependencies
testImplementation 'org.springframework.boot:spring-boot-starter-test'
+ testImplementation "com.redis:testcontainers-redis:${testcontainersRedisVersion}"
+ testImplementation "org.testcontainers:junit-jupiter:1.20.4"
}
// Use -parameters flag for Spring
diff --git a/demos/roms-vectorizers/build.gradle b/demos/roms-vectorizers/build.gradle
index 3a95d80df..bd09614bd 100644
--- a/demos/roms-vectorizers/build.gradle
+++ b/demos/roms-vectorizers/build.gradle
@@ -59,8 +59,10 @@ dependencies {
// Test dependencies
testImplementation 'org.springframework.boot:spring-boot-starter-test'
+ testImplementation 'org.springframework.boot:spring-boot-resttestclient'
+ testImplementation 'org.springframework.boot:spring-boot-data-redis'
testImplementation "com.redis:testcontainers-redis:${testcontainersRedisVersion}"
- testImplementation "org.testcontainers:junit-jupiter"
+ testImplementation "org.testcontainers:junit-jupiter:1.20.4"
testImplementation 'org.mockito:mockito-core'
}
diff --git a/demos/roms-vectorizers/src/test/java/com/foogaro/vectorizers/config/TestRedisConfiguration.java b/demos/roms-vectorizers/src/test/java/com/foogaro/vectorizers/config/TestRedisConfiguration.java
index 2ec9b2fbf..4df510c96 100644
--- a/demos/roms-vectorizers/src/test/java/com/foogaro/vectorizers/config/TestRedisConfiguration.java
+++ b/demos/roms-vectorizers/src/test/java/com/foogaro/vectorizers/config/TestRedisConfiguration.java
@@ -4,7 +4,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
-import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.boot.data.redis.autoconfigure.DataRedisAutoConfiguration;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
@@ -24,7 +24,7 @@
@TestConfiguration
@AutoConfigureAfter(
- RedisAutoConfiguration.class
+ DataRedisAutoConfiguration.class
)
@Testcontainers
@Disabled(
diff --git a/demos/roms-vectorizers/src/test/java/com/foogaro/vectorizers/controller/TestTextDataController.java b/demos/roms-vectorizers/src/test/java/com/foogaro/vectorizers/controller/TestTextDataController.java
index 77094b20c..5b017b39c 100644
--- a/demos/roms-vectorizers/src/test/java/com/foogaro/vectorizers/controller/TestTextDataController.java
+++ b/demos/roms-vectorizers/src/test/java/com/foogaro/vectorizers/controller/TestTextDataController.java
@@ -13,7 +13,7 @@
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.boot.resttestclient.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
diff --git a/demos/roms-vss-movies/build.gradle b/demos/roms-vss-movies/build.gradle
index 94c2ccc52..cdd99d34b 100644
--- a/demos/roms-vss-movies/build.gradle
+++ b/demos/roms-vss-movies/build.gradle
@@ -53,6 +53,8 @@ dependencies {
// Test dependencies
testImplementation 'org.springframework.boot:spring-boot-starter-test'
+ testImplementation "com.redis:testcontainers-redis:${testcontainersRedisVersion}"
+ testImplementation "org.testcontainers:junit-jupiter:1.20.4"
}
// Use -parameters flag for Spring
diff --git a/demos/roms-vss/build.gradle b/demos/roms-vss/build.gradle
index 940a9423b..b76b8a7a2 100644
--- a/demos/roms-vss/build.gradle
+++ b/demos/roms-vss/build.gradle
@@ -54,6 +54,8 @@ dependencies {
// Test dependencies
testImplementation 'org.springframework.boot:spring-boot-starter-test'
+ testImplementation "com.redis:testcontainers-redis:${testcontainersRedisVersion}"
+ testImplementation "org.testcontainers:junit-jupiter:1.20.4"
}
// Use -parameters flag for Spring
diff --git a/docs/content/modules/ROOT/pages/migration-guide.adoc b/docs/content/modules/ROOT/pages/migration-guide.adoc
index bf54d1d70..f123fcfe8 100644
--- a/docs/content/modules/ROOT/pages/migration-guide.adoc
+++ b/docs/content/modules/ROOT/pages/migration-guide.adoc
@@ -3,6 +3,174 @@
This guide helps you migrate between major versions of Redis OM Spring.
+== Upgrading to 2.0.0 (Spring Boot 4.0.0)
+
+Redis OM Spring 2.0.0 is a major version upgrade that brings support for Spring Boot 4.0.0 and its ecosystem.
+
+=== Dependency Changes
+
+[cols="1,1,1"]
+|===
+|Dependency |Previous Version |New Version
+
+|Spring Boot
+|3.5.8
+|4.0.0
+
+|Spring Data Redis
+|3.5.6
+|4.0.0
+
+|Spring Framework
+|6.2.x
+|7.0.x
+
+|Jedis
+|6.0.0
+|7.0.0
+
+|Java
+|17+
+|21+ (required)
+|===
+
+=== Breaking Changes
+
+==== Java 21 Required
+
+**Impact:** All users.
+
+Spring Boot 4.0 requires Java 21 as the minimum version. You must upgrade your JDK to Java 21 or later.
+
+**Migration Steps:**
+
+. Install Java 21 (e.g., from https://adoptium.net/[Eclipse Temurin] or your preferred JDK distribution)
+. Update your build configuration:
++
+.Maven (pom.xml)
+[source,xml]
+----
+
+ 21
+
+----
++
+.Gradle (build.gradle)
+[source,groovy]
+----
+java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(21)
+ }
+}
+----
+
+==== Spring Boot 4.0 Modularization
+
+**Impact:** Users with test dependencies or custom auto-configuration.
+
+Spring Boot 4.0 has reorganized packages to improve modularity. Several test-related classes have moved to new modules.
+
+**Migration Steps:**
+
+If you use any of the following in your tests, update your imports and add the appropriate test dependencies:
+
+.AutoConfigureMockMvc
+[source,java]
+----
+// Before (3.x)
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+
+// After (4.0)
+import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
+----
+
+Add test dependency:
+[source,xml]
+----
+
+ org.springframework.boot
+ spring-boot-starter-webmvc-test
+ test
+
+----
+
+.TestRestTemplate
+[source,java]
+----
+// Before (3.x)
+import org.springframework.boot.test.web.client.TestRestTemplate;
+
+// After (4.0)
+import org.springframework.boot.resttestclient.TestRestTemplate;
+----
+
+Add test dependency:
+[source,xml]
+----
+
+ org.springframework.boot
+ spring-boot-resttestclient
+ test
+
+----
+
+.DataRedisAutoConfiguration
+[source,java]
+----
+// Before (3.x)
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+
+// After (4.0)
+import org.springframework.boot.data.redis.autoconfigure.DataRedisAutoConfiguration;
+----
+
+Add dependency if needed:
+[source,xml]
+----
+
+ org.springframework.boot
+ spring-boot-data-redis
+
+----
+
+==== Repository Auto-Configuration Changes
+
+**Impact:** Users with multiple Redis repository configurations or custom auto-configuration exclusions.
+
+Spring Boot 4.0's auto-configuration for Redis repositories is more aggressive and may create conflicting beans if you're using custom repository configurations.
+
+**Migration Steps:**
+
+If you encounter bean definition conflicts, exclude the auto-configuration:
+
+[source,java]
+----
+import org.springframework.boot.data.redis.autoconfigure.DataRedisRepositoriesAutoConfiguration;
+
+@SpringBootApplication(
+ exclude = DataRedisRepositoriesAutoConfiguration.class
+)
+public class MyApplication {
+ // ...
+}
+----
+
+=== Testing Your Migration
+
+After upgrading, ensure you test:
+
+. All repository methods and queries
+. Custom auto-configuration classes
+. Integration tests, especially those using TestContainers
+. Any direct usage of Spring Data Redis or Jedis APIs
+
+=== Additional Resources
+
+* https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Release-Notes[Spring Boot 4.0 Release Notes]
+* https://github.com/spring-projects/spring-data-redis/wiki/Spring-Data-Redis-4.0-Release-Notes[Spring Data Redis 4.0 Release Notes]
+* https://github.com/redis/jedis/releases/tag/v7.0.0[Jedis 7.0.0 Release Notes]
+
== Upgrading to 1.1.0 (Spring Boot 3.5.x)
Redis OM Spring 1.1.0 introduces support for Spring Boot 3.5.x and includes important dependency upgrades.
diff --git a/docs/content/modules/ROOT/pages/setup.adoc b/docs/content/modules/ROOT/pages/setup.adoc
index 1e0f3df63..7009c030d 100644
--- a/docs/content/modules/ROOT/pages/setup.adoc
+++ b/docs/content/modules/ROOT/pages/setup.adoc
@@ -9,11 +9,11 @@ This guide will help you set up Redis OM Spring in your project and configure it
Redis OM Spring has the following requirements:
-* *Java 17+*: Redis OM Spring requires JDK 17 or newer
-* *Spring Boot 3.0+*: Built on Spring Framework 6.0.11+
+* *Java 21+*: Redis OM Spring requires JDK 21 or newer
+* *Spring Boot 4.0+*: Built on Spring Framework 7.0.x
* *Redis 8.0.0+*: Requires Redis with JSON and Query Engine capabilities
-* *Jedis 5.2.0+* (included): Redis OM Spring uses Jedis as its Redis client
-* *Spring Data Redis 3.4.1+* (included): Built on Spring Data Redis
+* *Jedis 7.0.0+* (included): Redis OM Spring uses Jedis as its Redis client
+* *Spring Data Redis 4.0.0+* (included): Built on Spring Data Redis
== Setting up Redis
@@ -68,8 +68,8 @@ If you're starting a new project, you can create a basic Spring Boot application
2. Select:
* Project: Maven or Gradle
* Language: Java
- * Spring Boot: 3.0.0+
- * Java: 17+
+ * Spring Boot: 4.0.0+
+ * Java: 21+
3. Generate and download the project
@@ -85,14 +85,14 @@ Add the dependency to your `pom.xml`:
com.redis.omredis-om-spring
- 1.0.0-RC2
+ 2.0.0com.redis.omredis-om-spring-ai
- 1.0.0-RC2
+ 2.0.0
----
@@ -113,7 +113,7 @@ Redis OM Spring uses code generation to create metamodels. If you encounter issu
org.springframework.bootspring-boot-configuration-processor
- 3.3.0
+ 4.0.0org.projectlombok
@@ -123,7 +123,7 @@ Redis OM Spring uses code generation to create metamodels. If you encounter issu
com.redis.omredis-om-spring
- 1.0.0-RC2
+ 2.0.0
@@ -146,7 +146,7 @@ To use SNAPSHOT releases, add the snapshots repository to your `pom.xml`:
com.redis.omredis-om-spring
- 1.0.0-RC4-SNAPSHOT
+ 2.0.0-SNAPSHOT
----
@@ -157,7 +157,7 @@ Add the dependency to your `build.gradle`:
[source,groovy]
----
ext {
- redisOmVersion = '1.0.0-RC2'
+ redisOmVersion = '2.0.0'
}
dependencies {
@@ -217,7 +217,7 @@ repositories {
}
ext {
- redisOmVersion = '1.0.0-RC4-SNAPSHOT'
+ redisOmVersion = '2.0.0-SNAPSHOT'
}
----
diff --git a/docs/content/modules/ROOT/pages/version-requirements.adoc b/docs/content/modules/ROOT/pages/version-requirements.adoc
index 26eed0eae..0bb05621d 100644
--- a/docs/content/modules/ROOT/pages/version-requirements.adoc
+++ b/docs/content/modules/ROOT/pages/version-requirements.adoc
@@ -18,29 +18,29 @@ Redis OM Spring has the following version requirements:
|Notes
|Spring Boot
-|3.3.x
-|3.5.8 or later
-|Built with Spring Boot 3.5.8
+|4.0.0
+|4.0.0 or later
+|Built with Spring Boot 4.0.0
|Spring Data Redis
-|3.4.1
-|3.5.6 or later
+|4.0.0
+|4.0.0 or later
|Aligned with Spring Boot version
|Spring Framework
-|6.2.x
-|Latest 6.x
+|7.0.x
+|Latest 7.x
|Transitive via Spring Boot
|Jedis
-|6.0.0
-|6.0.0 or later
-|Redis Java client. **Breaking change in 6.0.0** - see xref:migration-guide.adoc[Migration Guide]
+|7.0.0
+|7.0.0 or later
+|Redis Java client
|Java
-|17
-|17 or 21
-|Spring Boot 3.x requires Java 17+
+|21
+|21
+|Spring Boot 4.x requires Java 21+
|Redis Stack
|6.2.x
@@ -58,11 +58,11 @@ Redis OM Spring follows an **N-2 support policy** for Spring Boot versions:
=== Example
-As of Redis OM Spring 1.1.0 (November 2025):
+As of Redis OM Spring 2.0.0 (November 2025):
-* **Built with**: Spring Boot 3.5.8
-* **Minimum supported**: Spring Boot 3.3.x (OSS support until June 2025)
-* **Recommended**: Spring Boot 3.5.x
+* **Built with**: Spring Boot 4.0.0
+* **Minimum supported**: Spring Boot 4.0.0
+* **Recommended**: Spring Boot 4.0.0 or later
[WARNING]
====
@@ -97,7 +97,7 @@ To determine which Spring Boot versions Redis OM Spring supports:
If you need to upgrade your Spring Boot version:
-1. Check the https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.x-Release-Notes[Spring Boot release notes] for breaking changes
+1. Check the https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Release-Notes[Spring Boot release notes] for breaking changes
2. Update your `pom.xml` or `build.gradle` to use the new version
3. Run your tests to ensure compatibility
4. Pay special attention to:
@@ -107,13 +107,13 @@ If you need to upgrade your Spring Boot version:
== Java Version Requirements
-Redis OM Spring requires Java 17 or higher because:
+Redis OM Spring requires Java 21 or higher because:
-* Spring Boot 3.x requires Java 17 as a minimum
-* We use Java 17 language features in our codebase
-* Java 17 is an LTS (Long Term Support) release
+* Spring Boot 4.x requires Java 21 as a minimum
+* We use Java 21 language features in our codebase
+* Java 21 is an LTS (Long Term Support) release
-We recommend using Java 17 or Java 21 (the next LTS) for production deployments.
+We recommend using Java 21 for production deployments.
== Redis Requirements
@@ -146,8 +146,8 @@ When using Redis OM Spring, ensure your dependencies are aligned:
[source,xml]
----
- 3.5.8
- 1.1.0
+ 4.0.0
+ 2.0.0
diff --git a/docs/package-lock.json b/docs/package-lock.json
index 9f03e40d3..78cb02199 100644
--- a/docs/package-lock.json
+++ b/docs/package-lock.json
@@ -1064,9 +1064,9 @@
}
},
"node_modules/glob": {
- "version": "10.4.5",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
- "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
"license": "ISC",
"dependencies": {
"foreground-child": "^3.1.0",
@@ -1446,9 +1446,9 @@
}
},
"node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
diff --git a/gradle.properties b/gradle.properties
index e199ee80d..d8a130357 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,14 +1,14 @@
-version = 1.1.1
+version = 2.0.0
group = com.redis.om
description = Redis OM Spring provides powerful repository and custom object-mapping abstractions built on top of the powerful Spring Data Redis (SDR) framework.
-springBootVersion = 3.5.8
+springBootVersion = 4.0.0
springDependencyVersion = 1.1.7
testcontainersRedisVersion = 2.2.4
-sdrVersion = 3.5.6
-jedisVersion = 6.0.0
+sdrVersion = 4.0.0
+jedisVersion = 7.0.0
cdi = 2.0-PFD
autoServiceVersion = 1.1.1
guavaVersion = 33.4.8-jre
diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/RedisModulesConfiguration.java b/redis-om-spring/src/main/java/com/redis/om/spring/RedisModulesConfiguration.java
index f866b4795..32e4e04b1 100644
--- a/redis-om-spring/src/main/java/com/redis/om/spring/RedisModulesConfiguration.java
+++ b/redis-om-spring/src/main/java/com/redis/om/spring/RedisModulesConfiguration.java
@@ -15,8 +15,6 @@
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
-import org.springframework.boot.autoconfigure.gson.GsonBuilderCustomizer;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
@@ -50,6 +48,7 @@
import com.redis.om.spring.search.stream.EntityStream;
import com.redis.om.spring.search.stream.EntityStreamImpl;
import com.redis.om.spring.serialization.gson.*;
+import com.redis.om.spring.serialization.gson.GsonBuilderCustomizer;
import com.redis.om.spring.vectorize.Embedder;
import com.redis.om.spring.vectorize.NoopEmbedder;
@@ -78,7 +77,7 @@
proxyBeanMethods = false
)
@EnableConfigurationProperties(
- { RedisProperties.class, RedisOMProperties.class }
+ { RedisOMProperties.class }
)
@EnableAspectJAutoProxy
@ComponentScan(
@@ -169,6 +168,23 @@ public GsonBuilder gsonBuilder(List customizers) {
return builder;
}
+ /**
+ * Creates the Gson instance from the configured GsonBuilder.
+ *
+ * This bean provides the Gson instance used by various components for JSON
+ * serialization and deserialization throughout the application.
+ *
+ * @param gsonBuilder the configured Gson builder
+ * @return the Gson instance
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ public com.google.gson.Gson gson(@Qualifier(
+ "omGsonBuilder"
+ ) GsonBuilder gsonBuilder) {
+ return gsonBuilder.create();
+ }
+
/**
* Creates the Redis modules client for accessing Redis Stack modules.
*
diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/convert/MappingRedisOMConverter.java b/redis-om-spring/src/main/java/com/redis/om/spring/convert/MappingRedisOMConverter.java
index 58113323c..bb69453c4 100644
--- a/redis-om-spring/src/main/java/com/redis/om/spring/convert/MappingRedisOMConverter.java
+++ b/redis-om-spring/src/main/java/com/redis/om/spring/convert/MappingRedisOMConverter.java
@@ -18,6 +18,7 @@
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.convert.CustomConversions;
+import org.springframework.data.core.TypeInformation;
import org.springframework.data.mapping.*;
import org.springframework.data.mapping.model.EntityInstantiator;
import org.springframework.data.mapping.model.EntityInstantiators;
@@ -33,7 +34,6 @@
import org.springframework.data.redis.core.mapping.RedisPersistentEntity;
import org.springframework.data.redis.core.mapping.RedisPersistentProperty;
import org.springframework.data.redis.util.ByteUtils;
-import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.*;
@@ -625,6 +625,36 @@ private void writeInternal(@Nullable String keyspace, String path, @Nullable Obj
return;
}
+ // Handle arrays by iterating and writing each element directly
+ if (value.getClass().isArray() && !(value instanceof byte[])) {
+ TypeInformation> componentType;
+ if (typeHint.getComponentType() != null) {
+ componentType = typeHint.getComponentType();
+ } else {
+ // For arrays, get the component type from the actual array class
+ Class> arrayComponentType = value.getClass().getComponentType();
+ componentType = TypeInformation.of(arrayComponentType);
+ }
+
+ int length = java.lang.reflect.Array.getLength(value);
+ for (int i = 0; i < length; i++) {
+ Object element = java.lang.reflect.Array.get(value, i);
+
+ // Break if we encounter a null element (consistent with collection handling)
+ if (element == null) {
+ break;
+ }
+
+ String currentPath = path + (path.isEmpty() ? "" : ".") + "[" + i + "]";
+ if (customConversions.hasCustomWriteTarget(element.getClass())) {
+ writeToBucket(currentPath, element, sink, componentType.getType());
+ } else {
+ writeInternal(keyspace, currentPath, element, componentType, sink);
+ }
+ }
+ return;
+ }
+
if (value.getClass() != typeHint.getType()) {
typeMapper.writeType(value.getClass(), sink.getBucket().getPropertyPath(path));
}
@@ -658,6 +688,14 @@ private void writeInternal(@Nullable String keyspace, String path, @Nullable Obj
.getRequiredComponentType(), sink);
} else {
+ // Check if collection contains nulls (either null elements or arrays with nulls)
+ // If so, skip writing the collection entirely
+ boolean hasNulls = collectionContainsNulls(propertyValue);
+ if (hasNulls) {
+ // Don't persist collections with nulls
+ return;
+ }
+
if (Iterable.class.isAssignableFrom(propertyValue.getClass())) {
writeCollection(entity.getType(), keyspace, propertyStringPath, (Iterable>) propertyValue,
@@ -1124,6 +1162,41 @@ private Object toArray(Collection