【问题标题】:Spring Boot: Redis CRUD Repository findById or findAll always returns Optional.empty (null)Spring Boot:Redis CRUD Repository findById 或 findAll 总是返回 Optional.empty (null)
【发布时间】:2021-09-27 22:14:54
【问题描述】:

大家好,

我正在使用 Spring Boot 2.3.12.RELEASE,它在内部使用 Spring Data Redis 2.3.9.RELEASE 作为托管依赖项。

当我尝试使用 Spring Boot CRUD 存储库将对象保存到 Redis 缓存时,它被存储而没有任何错误,我可以看到通过 Redis Manager 存储的对象。

但是,当我尝试使用相同的 id 获取相同的对象时,即使用 CRUD 存储库的 findById() 方法,我无法找到它。

此外,当我在同一个 CRUDRepository 对象上尝试 findAll() 时,我得到 Optional.empty 结果,这很奇怪,因为 findAll() 应该返回存储库中存在的所有记录。

我在下面添加了配置、存储库和模型类代码以及一些屏幕截图供您阅读。

请注意:我知道这个平台上有很多与此问题相关的类似问题,我也尝试了这些问题中提到的解决方案,但这对我不起作用。 p>

这个问题的任何解决方案都会很有帮助。

模型类:

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;

}

存储库:

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 配置类:

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 管理器截图:

Eclipse IDE - 调试屏幕截图:

【问题讨论】:

    标签: java spring spring-boot redis spring-data-redis


    【解决方案1】:

    好吧,我也在 GitHub 上向 spring-data-redis 存储库提出了一个缺陷,但该缺陷被该存储库的维护者之一关闭,甚至没有发布任何适当的解决方案。他只是引用了一个现有问题,该问题甚至在没有发布任何解决方案的情况下就关闭了。这是该问题的链接。

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

    因此,在进行一些研究时,我发现了一个我在此分享的解决方案,该解决方案适用于我的案例。

    解决方案不是使用 Spring Boot 实现的默认 CRUD 存储库方法,而是编写您自己的存储库类,其中包含符合您标准的方法来存储和从 Redis 缓存中获取数据。就是这样,现在您应该能够在项目中使用存储库方法存储/获取数据了。

    我在下面发布一个示例以供参考。

    自定义存储库界面

    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);
    }
    

    自定义存储库接口实现

    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;
        }
    }
    

    希望这对将来可能遇到此问题的其他人有所帮助。

    此外,还有另一种方法可以解决此问题,即切换到其他库提供程序,例如 Redisson,但是,我还没有尝试过,所以如果您愿意,可以尝试检查一下。

    【讨论】:

      【解决方案2】:

      您需要为您的实体提供相同的包, 我通过提取一个库并将我的实体放在那里解决了这个问题

      你会在这里找到一个解释: https://github.com/spring-projects/spring-data-redis/issues/2114

      【讨论】:

      猜你喜欢
      • 2017-06-24
      • 2023-03-12
      • 2019-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-19
      • 1970-01-01
      相关资源
      最近更新 更多