skip to Main Content

Hi Team,

I am using Spring Boot 2.3.12.RELEASE which internally uses Spring Data Redis 2.3.9.RELEASE as a managed dependency.

When I am trying to save an object to the Redis cache using Spring Boot CRUD repository, it is getting stored without any error and I can see the object stored via Redis Manager.

However, when I try to fetch the same object using the same id i.e. using findById() method of CRUD repository, I am unable to find it.

Moreover, when I try findAll() on the same CRUDRepository object I get Optional.empty result which is strange as findAll() should return all records present in the repository.

I have added the configuration, repository and model class codes and some screenshots below for your perusal.

Please Note: I know there are many similar questions asked on this platform related to this issue and also I tried the solutions mentioned on such questions, but that didn’t work for me.

Any solutions for this issue will be really helpful.

Model Class:

package com.test.cache.entity;
 
import java.util.concurrent.TimeUnit;
 
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.redis.core.RedisHash;
import org.springframework.data.redis.core.TimeToLive;
import org.springframework.data.redis.core.index.Indexed;
 
import lombok.AllArgsConstructor;
import lombok.Data;
 
@Data
@AllArgsConstructor
@RedisHash("OTPValidationLogCache")
public class OTPValidationLogCache {
 
@Id
@Indexed
private String id;

@Indexed
private int validationFailureCount;
 
@TimeToLive(unit = TimeUnit.MILLISECONDS)
private long expiry;

}

Repository:

package com.test.cache.repository;
 
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
 
import com.test.cache.entity.OTPValidationLogCache;
 
@Repository
public interface OTPValidationLogCacheRepository extends CrudRepository<OTPValidationLogCache, String> {
 
}

Redis Configuration Class:

package com.test.configuration;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisPassword;
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.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
 
import java.time.Duration;
 
@Configuration
@EnableRedisRepositories(basePackages = "com.test")
public class RedisConfig {
 
public static  final long REDIS_CONNECT_TIMEOUT_SECS = 10L;
 
@Bean
public RedisStandaloneConfiguration redisStandaloneConfiguration() {
final RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName("*******");
redisStandaloneConfiguration.setPort(6379);
redisStandaloneConfiguration.setPassword(RedisPassword.of("**********"));
//Credentials hidden for code sharing purpose.
return redisStandaloneConfiguration;
}
 
@Bean
public JedisConnectionFactory redisConnectionFactory() {
final JedisClientConfiguration jedisClientConfiguration = JedisClientConfiguration.builder()
.connectTimeout(Duration.ofSeconds(REDIS_CONNECT_TIMEOUT_SECS))
.useSsl()
.build();
 
    return new JedisConnectionFactory(redisStandaloneConfiguration(), jedisClientConfiguration);
}
 
@Bean
public RedisTemplate<String, Object> redisTemplate() {
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(redisConnectionFactory());
    return template;
}
}

Redis Manager Screenshot:

Values Present Can Be Seen In Redis Manager

Eclipse IDE – Screenshot of Debugging Screen:

CRUD Repository Returning Empty Value

2

Answers


  1. Chosen as BEST ANSWER

    Well, I also raised a defect to spring-data-redis repository on GitHub for the same but the defect got closed by one of the maintainers of this repository without even posting any proper solution. He just gave a reference to an existing issue that was even closed without posting any solution. Here is the link to that issue.

    https://github.com/spring-projects/spring-data-redis/issues/2130

    Hence, while doing some research, I came across a solution that I am sharing here which worked in my case.

    The solution is not to use the default CRUD repository methods implemented by Spring Boot, instead, write your own repository class having methods with your criteria to store and fetch the data from the Redis cache. That's it, now you should be able to store/fetch the data using the repository methods across your project.

    I am posting an example below for reference.

    Custom Repository Interface

    package com.test.cache.repository;
    
    import java.io.IOException;
    import java.util.Map;
    
    import com.test.cache.entity.OTPValidationLogCache;
    
    public interface OTPValidationLogCacheRepository {
    
        void save(OTPValidationLogCache customer);
        OTPValidationLogCache find(Long id);
        Map<?,?> findAll() throws IOException;
        void update(OTPValidationLogCache customer);
        void delete(Long id);
    }
    

    Custom Repository Interface Implementation

    package com.test.cache.repository;
    
    import java.io.IOException;
    import java.time.Duration;
    import java.util.Map;
    import java.util.concurrent.atomic.AtomicInteger;
    
    import javax.annotation.PostConstruct;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.Cursor;
    import org.springframework.data.redis.core.HashOperations;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.ScanOptions;
    import org.springframework.data.redis.core.ScanOptions.ScanOptionsBuilder;
    import org.springframework.data.repository.CrudRepository;
    import org.springframework.stereotype.Repository;
    
    import com.test.cache.entity.OTPValidationLogCache;
    import com.test.configuration.AppConfig;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.common.collect.Maps;
    
    @Repository
    public class OTPValidationLogCacheRepositoryImpl implements OTPValidationLogCacheRepository {
        
        private String key;
        
        private RedisTemplate redisTemplate;
        private HashOperations hashOperations;
        private ObjectMapper objMapper;
        
        @Autowired
        public OTPValidationLogCacheRepositoryImpl(RedisTemplate redisTemplate, ObjectMapper objmapper) {
            this.redisTemplate = redisTemplate;
            this.objMapper = objmapper;
        }
        
        @PostConstruct
        private void init() {
            hashOperations = redisTemplate.opsForHash();
        }
        
        @Override
        public void save(OTPValidationLogCache otpvalCache) {
            hashOperations.put(key.concat(otpvalCache.getId().toString()), otpvalCache.getId(), otpvalCache);
            setExpiryTime(key.concat(String.valueOf(otpvalCache.getId())), AppConfig.getUserBanDurationInSeconds());
        }
    
        @Override
        public OTPValidationLogCache find(Long id) {
            return (OTPValidationLogCache) hashOperations.get(key.concat(String.valueOf(id)), id);
        }
    
        @Override
        public Map findAll() throws IOException {
            Map<Integer, OTPValidationLogCache> values = Maps.newHashMap();
            Cursor c =  hashOperations.scan(OTPValidationLogCache.class, new ScanOptionsBuilder().match(key.concat("*")).build());
            AtomicInteger count = new AtomicInteger(1);
            c.forEachRemaining(element ->
                    {
                        values.put(count.getAndIncrement(), objMapper.convertValue(element, OTPValidationLogCache.class));
                    }
                    );
            c.close();
            return values;
        }
    
        @Override
        public void update(OTPValidationLogCache customer) {
            hashOperations.put(key, customer.getId(), customer);
        }
    
        @Override
        public void delete(Long id) {
            hashOperations.delete(key, id);
        }
        
        private void setExpiryTime(String key, Long timeout)
        {
            redisTemplate.expire(key, Duration.ofSeconds(timeout));
        }
    
        public synchronized void setKey(String key)
        {
            this.key = key;
        }
    }
    

    Hope this helps others who may encounter this issue in the future.

    Also, there is one more alternative available for this issue, that is switching to a different library provider such as Redisson, however, I have not tried it yet, so if you want, you may try and check.


  2. You need to have the same package for your entities ,
    I resolved the problem by extracting a lib and putting my entities there

    You would find an explication here :
    https://github.com/spring-projects/spring-data-redis/issues/2114

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search