关于Redis-存Long取Integer类型转换错误的问题

文章讲述了在使用Redis时遇到的两个问题:Long类型存储后取出时出现类型转换错误,原因是GenericJackson2JsonRedisSerializer将所有数据反序列化为Object,丢失了原始类型信息;另一个是String对象的反序列化失败,因为非JSON格式的数据无法被Jackson解析。解决方案是针对Long类型存储时指定类型,对于String则需确保数据格式正确。

背景

最近遇到了两个Redis相关的问题,趁着清明假期,梳理整理。

1.存入Long类型对象,在代码中使用Long类型接收,结果报类型转换错误。

2.String对象的反序列化问题,直接在Redis服务器上新增一个key-value,而后在代码中get(key)时,报反序列化失败。
关于Long类型转换错误
Redis的配置如下

Redis中序列化相关的配置,我这里采用的是GenericJackson2JsonRedisSerializer类型的序列化方式(这种方式会有一个类型转换的坑,下面会提到)

    @Configuration
    @AutoConfigureAfter(RedisAutoConfiguration.class)
    public class RedisConfiguration {
     
        @Bean
        public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory redisConnectionFactory) {
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(redisConnectionFactory);
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
            redisTemplate.afterPropertiesSet();
            return redisTemplate;
        }
    }  


存入Long对象取出Integer对象

测试方法如下

    @Test
    public void redisSerializerLong(){
        try {
            Long longValue = 123L;
            redisLongCache.set("cacheLongValue",longValue);
            Object cacheValue = redisLongCache.get("cacheLongValue");
            Long a = (Long) cacheValue;
        }catch (ClassCastException e){
            e.printStackTrace();
        }
    }


会报类型转换错误java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long。

为什么类型会变为Integer呢?跟我一起追踪源码,便会发现问题。

1. 在代码的最外层获取redis中key对应的value值

redisTemplate.opsForValue().get(key);

2.在DefaultValueOperations类中的get(Object key)方法

    public V get(Object key) {
     
        return execute(new ValueDeserializingRedisCallback(key) {
     
            @Override
            protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
                return connection.get(rawKey);
            }
        }, true);
    }


3.打断点继续往里跟,RedisTemplate中的execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline)方法里面,有一行关键代码。

T result = action.doInRedis(connToExpose); 

此为获取redis中对应的value值,并对其进行反序列化操作。

4.在抽象类AbstractOperations<K, V>中,定义了反序列化操作,对查询结果result进行反序列化。

    public final V doInRedis(RedisConnection connection) {
        byte[] result = inRedis(rawKey(key), connection);
        return deserializeValue(result);
    }


V deserializeValue(byte[] value)反序列化

    V deserializeValue(byte[] value) {
        if (valueSerializer() == null) {
            return (V) value;
        }
        return (V) valueSerializer().deserialize(value);
    }


 5.终于到了具体实现类GenericJackson2JsonRedisSerializer

    public Object deserialize(@Nullable byte[] source) throws SerializationException {
        return deserialize(source, Object.class);
    }


实现反序列化方法,注意!这里统一将结果反序列化为Object类型,所以这里便是问题的根源所在,对于数值类型,取出后统一转为Object,导致泛型类型丢失,数值自动转为了Integer类型也就不奇怪了。

    public <T> T deserialize(@Nullable byte[] source, Class<T> type) throws SerializationException {
     
        Assert.notNull(type,
                "Deserialization type must not be null! Pleaes provide Object.class to make use of Jackson2 default typing.");
     
        if (SerializationUtils.isEmpty(source)) {
            return null;
        }
     
        try {
            return mapper.readValue(source, type);
        } catch (Exception ex) {
            throw new SerializationException("Could not read JSON: " + ex.getMessage(), ex);
        }
    }  


String对象转义问题 

测试方法

    @Test
    public void redisSerializerString() {
        try {
            String stringValue = "abc";
            redisStringCache.set("codeStringValue", stringValue);
            String cacheValue = redisStringCache.get("codeStringValue");     // 序列化失败
            String serverInsert = redisStringCache.get("serverInsertValue");
            if (Objects.equals(cacheValue, serverInsert)) {
                System.out.println("serializer ok");
            } else {
                System.out.println("serializer err");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


提前在redis服务器上插入一个非Json格式的String对象

直接在Redis服务器上使用set命令新增一对Key-Value,在代码中取出会反序列化失败, 

    org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Unrecognized token 'abc': was expecting ('true', 'false' or 'null')
     at [Source: (byte[])"abc"; line: 1, column: 7]; nested exception is com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'abc': was expecting ('true', 'false' or 'null')
     at [Source: (byte[])"abc"; line: 1, column: 7]
        at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.deserialize(GenericJackson2JsonRedisSerializer.java:132)
        at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.deserialize(GenericJackson2JsonRedisSerializer.java:110)
        at org.springframework.data.redis.core.AbstractOperations.deserializeValue(AbstractOperations.java:334)
        at org.springframework.data.redis.core.AbstractOperations$ValueDeserializingRedisCallback.doInRedis(AbstractOperations.java:60)
        at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:224)
        at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:184)
        at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:95)
        at org.springframework.data.redis.core.DefaultValueOperations.get(DefaultValueOperations.java:48)  


小总结

这个问题是因为,自己在测试的过程中,没有按照代码流程执行,想当然的认为,代码跑出来的结果和自己手动插入的结果是一样的。

在相关的测试验证过程中应该严格的控制变量,不能凭借下意识的决断来操作,谨记软件之事——必作于细!
————————————————

<think>好的,我现在要解决用户的问题:“Redis报错返回err integer”。首先,我需要理解这个错误的具体情况。用户在使用Redis时遇到了错误信息“ERR value is not an integer or out of range”,需要知道原因和解决方法。 首先,回忆一下Redis的常见命令和相关错误。这个错误通常发生在执行需要整数参数的命令时,但提供的参数不是整数或者超出了允许的范围。比如INCR、DECR、INCRBY等命令要求操作的值是整数,或者当设置过期时间时,时间参数不是整数。 接下来,分步骤分析可能的原因: 1. **值类型不正确**:比如对一个非整数类型的值执行INCR操作。例如,如果键对应的值是字符串"abc",执行INCR会失败,因为无法转换为整数。 2. **参数类型错误**:某些命令需要整数参数,但传递了非整数。比如EXPIRE命令的第二个参数应该是整数秒数,如果传了字符串就会报错。 3. **数值超出范围**:比如使用SETBIT命令时,偏移量超过了允许的范围(0到2^32-1),或者EXPIRE的时间参数是负数等。 然后,针对每个原因给出解决方法: - **检查值类型**:在INCR/DECR前,先GET键的值,确保是整数。如果不是,可能需要先SET为整数。 - **确保参数类型正确**:在调用命令时,参数必须是整数。比如在Java中,使用Jedis客户端时,传递的参数应该是整数类型,而不是字符串。 - **检查数值范围**:确认数值在有效范围内,如偏移量、过期时间等是否合理。 接下来,用户可能需要具体的代码示例。例如,在Java中使用Jedis处理INCR时,如何避免错误。比如,先检查键是否在,或者捕获异常处理。 然后,给出示例代码,展示正确的使用方式和错误处理。例如,正确的INCR操作,以及如何处理类型错误的情况。 最后,提出相关问题,帮助用户进一步理解相关知识点。例如,Redis的数据类型、INCR命令的具体行为、如何检查键的类型、处理过期时间错误等。 总结整个思考过程,确保覆盖错误的各种可能性,并提供清晰的解决步骤和代码示例,帮助用户实际解决问题。同时,通过相关问题引导用户深入理解Redis的使用和常见错误处理。</think><< Redis报错"ERR value is not an integer or out of range"通常发生在以下场景: 1. **类型不匹配操作**: 当尝试对非整数类型的值执行INCR/DECR操作时: ```java // 错误示例 jedis.set("counter", "abc"); jedis.incr("counter"); // 抛出错误 ``` 2. **无效参数格式**: 使用非整数参数执行需要整数的命令: ```java // 错误示例(EXPIRE需要整数秒数) jedis.expire("key", "60"); // 应该传入long类型 ``` 3. **数值越界**: 当数值超过Redis支持的范围(-9223372036854775808到9223372036854775807): ```java // 错误示例(超出Long范围) jedis.set("big_num", "9223372036854775808"); jedis.incr("big_num"); ``` **解决方案**: 1. 确保操作的值是整数类型: ```java // 正确操作示例 jedis.set("counter", "10"); Long result = jedis.incr("counter"); ``` 2. 验证参数类型: ```java // 正确设置过期时间 jedis.expire("key", 60L); // 显式使用long类型 ``` 3. 检查数值范围: ```java // 安全数值操作 String value = jedis.get("counter"); if (value != null && value.matches("-?\\d+")) { jedis.incr("counter"); } ``` 4. 异常处理: ```java try { jedis.incr("non_integer_key"); } catch (JedisDataException e) { System.out.println("处理非整数值:" + e.getMessage()); jedis.set("non_integer_key", "0"); // 重置为合法值 } ``` >>
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农汉子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值