关于Redis-存Long取Integer类型转换错误的问题;String对象被转义的问题

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 关于Redis-存Long取Integer类型转换错误的问题;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)  

小总结

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

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

 

参考资料:

https://blog.csdn.net/f641385712/article/details/84679456


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
14天前
|
Java
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
本文深入探讨了Java中方法参数的传递机制,包括值传递和引用传递的区别,以及String类对象的不可变性。通过详细讲解和示例代码,帮助读者理解参数传递的内部原理,并掌握在实际编程中正确处理参数传递的方法。关键词:Java, 方法参数传递, 值传递, 引用传递, String不可变性。
32 1
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
|
6天前
|
存储 消息中间件 NoSQL
Redis 数据结构与对象
【10月更文挑战第15天】在实际应用中,需要根据具体的业务需求和数据特点来选择合适的数据结构,并合理地设计数据模型,以充分发挥 Redis 的优势。
39 8
|
10天前
|
NoSQL Redis
Redis 字符串(String)
10月更文挑战第16天
25 4
|
22天前
|
存储 分布式计算 NoSQL
大数据-40 Redis 类型集合 string list set sorted hash 指令列表 执行结果 附截图
大数据-40 Redis 类型集合 string list set sorted hash 指令列表 执行结果 附截图
23 3
|
25天前
|
JSON 缓存 NoSQL
Redis 在线查看序列化对象技术详解
Redis 在线查看序列化对象技术详解
28 2
|
1月前
|
存储 JavaScript 前端开发
JavaScript 字符串(String) 对象
JavaScript 字符串(String) 对象
38 3
|
2月前
|
存储 JSON NoSQL
redis基本数据结构(String,Hash,Set,List,SortedSet)【学习笔记】
这篇文章是关于Redis基本数据结构的学习笔记,包括了String、Hash、Set、List和SortedSet的介绍和常用命令。文章解释了每种数据结构的特点和使用场景,并通过命令示例演示了如何在Redis中操作这些数据结构。此外,还提供了一些练习示例,帮助读者更好地理解和应用这些数据结构。
redis基本数据结构(String,Hash,Set,List,SortedSet)【学习笔记】
|
3月前
|
SQL 分布式计算 DataWorks
DataWorks产品使用合集之如何将STRING类型转换为DATETIME类型
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
|
3月前
|
C# 开发者 UED
WPF开发者必备秘籍:深度解析文件对话框使用技巧,打开与保存文件原来如此简单!
【8月更文挑战第31天】在WPF应用开发中,文件操作是常见需求。本文详细介绍了如何利用`Microsoft.Win32`命名空间下的`OpenFileDialog`和`SaveFileDialog`类来正确实现文件打开与保存功能。通过示例代码展示了如何设置文件过滤器、初始目录等属性,并使用对话框进行文件读写操作。正确使用文件对话框能显著提升用户体验,使应用更友好易用。
64 0
|
3月前
|
存储 JSON NoSQL
揭秘Redis字符串String的隐藏技能!从基础到进阶,让你的数据存储操作秒变高大上!
【8月更文挑战第24天】Redis中的字符串类型作为其基石,不仅能够存储从简单文本到复杂格式如JSON的各种数据,还能通过多样化的命令实现包括但不限于自增自减、设置过期时间等高级功能,极大提升了其实用性和灵活性。例如,使用`SET`命令可添加或更新键值对,`GET`获取值,`DEL`删除键;同时,`INCR`和`DECR`支持对整数值的原子性增减操作,非常适合实现计数器等功能;通过`EXPIRE`命令设置过期时间,则适用于需要限时存储的应用场景。尽管名为“字符串”,但实际上还可存储图片、音频文件的Base64编码等形式的数据,为开发者提供了强大而灵活的工具。
47 0