使用Redis获取数据转json,解决动态泛型传参的问题
场景:
项目有两种角色需要不同的登录权限,将redis做为用户登录信息缓存数据库。码一个方法,希望能够根据传入不用用户实体类型来获取相应的数据。用户实体为:SessionEntity<User1>、SessionEntity<User2>。json使用FastJson。
先阐述遇到的几个问题:
1、redis获取到的数据序列化后,转json,经常提示转换异常(并不是每次,只是时常)。
2、不想每种用户都书写一个redis操作方法(显得tai low)。
解决:
1、redis获取到的数据序列化后,转json,经常提示转换异常:
先说redis有两种获取方式。
1)
redisTemplate.opsForValue().get(key);
2)
SessionEntity result = redisTemplate.execute(new RedisCallback<SessionEntity>() { public SessionEntity doInRedis(RedisConnection connection) throws DataAccessException { RedisSerializer<String> serializer = getRedisSerializer(); byte[] key = serializer.serialize(s); byte[] value = connection.get(key); if (value == null) { return null; } String json = serializer.deserialize(value); return JSONObject.parseObject(json,SessionEntity.class); } });
显然第一种的方式比较简单。查看源码,发现第一种方式底层调用的也是redisTemplate.execute方法,所以应该算是一种封装吧。我们一直采用的是第二种方式。(第一种方式试过,也一样会出现json强转异常)。这里出现过json异常,怀疑是跟泛型有关。这里手动指定泛型反序列化类型。
修改后:
SessionEntity result = redisTemplate.execute(new RedisCallback<SessionEntity>() { public SessionEntity doInRedis(RedisConnection connection) throws DataAccessException { RedisSerializer<String> serializer = getRedisSerializer(); byte[] key = serializer.serialize(s); byte[] value = connection.get(key); if (value == null) { return null; } String json = serializer.deserialize(value); return JSONObject.parseObject(json, new TypeReference<SessionEntity<User>>(){}); } });
完美~,确实解决了json强转异常。
那么问题来了,这里的TypeReference需要手动指定明确的的实体类型,尝试添加泛型:
SessionEntity<T> result = redisTemplate.execute(new RedisCallback<SessionEntity<T>>() { public SessionEntity<T> doInRedis(RedisConnection connection) throws DataAccessException { RedisSerializer<String> serializer = getRedisSerializer(); byte[] key = serializer.serialize(s); byte[] value = connection.get(key); if (value == null) { return null; } String json = serializer.deserialize(value); return JSONObject.parseObject(json, new TypeReference<SessionEntity<T>>(){}); } });
看样子是没什么问题,而且泛型也被识别到了。 但是依旧无法通过。
2、不想每种用户都书写一个redis操作方法:
上面说到就算加了泛型也依旧无法通过,尝试了多种方式依旧如此。百度了一圈,都是说使用TypeReference这个来解决,但是并没有提及动态泛型的问题。偶然间看到文章说Fastjson不支持,所以尝试替换成jackson。
替换后的代码:
SessionEntity<T> result = redisTemplate.execute(new RedisCallback<SessionEntity<T>>() { public SessionEntity<T> doInRedis(RedisConnection connection) throws DataAccessException { RedisSerializer<String> serializer = getRedisSerializer(); byte[] key = serializer.serialize(s); byte[] value = connection.get(key); if (value == null) { return null; } String json = serializer.deserialize(value); ObjectMapper om = new ObjectMapper(); JavaType javatype = om.getTypeFactory().constructParametricType(SessionEntity.class, clazz); try { return om.readValue(json, javatype); } catch (IOException e) { e.printStackTrace(); } return null; // return JSONObject.parseObject(json, new TypeReference<SessionEntity<T>>(){}); } });
这里使用到了jackson的ObjectMapper。ObjectMapper类是Jackson库的主要类。它提供一些功能将转换成Java对象匹配JSON结构,反之亦然。它使用JsonParser和JsonGenerator的实例实现JSON实际的读/写。(复制来的)发现问题解决。
提供的抽象方法为:
public <T> SessionEntity<T> get(final String s, Class<T> clazz);
调用方式为:
sessionEntityDao.get(key, User1.class); 跟 sessionEntityDao.get(key, User2.class);
由于这里使用到的是jackson-databind-2.6.0的库,这个版本种constructParametricType这个方法已经快要过时,更高版本使用
constructParametrizedType
替换。这里我还没尝试过,等有空再玩。
这里问题已经解决,纯粹做个笔记以供自己以后方便查阅。这里只提供自己项目中遇到的解决方式之一,相信应该还有其他方式可以解决。如果有说明错误的地方,请指出并见谅。
补充知识:Redis爬坑——Redis实现通用序列化器 & 解决Redis反序列化失败
Redis默认序列化是 JdkSerializationRedisSerializer,由此可见
public void afterPropertiesSet() { super.afterPropertiesSet(); boolean defaultUsed = false; if (this.defaultSerializer == null) { this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader()); } if (this.enableDefaultSerializer) { if (this.keySerializer == null) { this.keySerializer = this.defaultSerializer; defaultUsed = true; } if (this.valueSerializer == null) { this.valueSerializer = this.defaultSerializer; defaultUsed = true; } if (this.hashKeySerializer == null) { this.hashKeySerializer = this.defaultSerializer; defaultUsed = true; } if (this.hashValueSerializer == null) { this.hashValueSerializer = this.defaultSerializer; defaultUsed = true; } } if (this.enableDefaultSerializer && defaultUsed) { Assert.notNull(this.defaultSerializer, "default serializer null and not all serializers initialized"); } if (this.scriptExecutor == null) { this.scriptExecutor = new DefaultScriptExecutor(this); } this.initialized = true; }
这里因为我们的项目需要更改默认序列策略为Jackson2JsonRedisSerializer让它序列化为可视化的***json***语句
我们首先定义自己的RedisTemplate,这里我们不要为了每一个类定义一个序列化器,我们定义一个统一的序列化器所以这里泛型是 <String,Object>,key我们使用StringRedisSerializer,value使用Jackson2JsonRedisSerializer
注释代码为修复反序列化bug的代码
@Bean public RedisTemplate<String, Object> objectRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate<String, Object> template = new RedisTemplate(); Jackson2JsonRedisSerializer<Object> jsonSerial = new Jackson2JsonRedisSerializer(Object.class); // //修复反序列化bug // ObjectMapper om = new ObjectMapper(); // om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); // jsonSerial.setObjectMapper(om); template.setDefaultSerializer(jsonSerial); template.setKeySerializer(RedisSerializer.string()); template.setConnectionFactory(redisConnectionFactory); template.afterPropertiesSet(); return template; }
测试代码为
@Test public void redisSaveObject(){ UserDO ob = new UserDO(); ob.setName("name"); ob.setCity("city"); objectRedisTemplate.opsForValue().set("ob1",ob); Object ob2 = objectRedisTemplate.opsForValue().get("ob1"); UserDO ob1 = (UserDO)ob2; System.out.println(ob1); }
运行结果为
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.hcy.core.model.UserDO at com.hcy.core.redistest.RedisTest.redisSaveObject(RedisTest.java:42) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at
很明显是对象强制转换错误
这是因为泛型的原因,redis在序列化时候把他当成Object序列化的,所以这里反序列化为Object是可以的,但是因为这个Object没有类型定义所以无法强转。
解决办法
在RedisTemplate中对序列化器Jackson2JsonRedisSerializer进行修改添加如下代码,上文注释了
//修复反序列化bug ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jsonSerial.setObjectMapper(om);
通过 objectMapper.enableDefaultTyping() 方法设置
即使使用 Object.class 作为 jcom.fasterxml.jackson.databind.JavaType 也可以实现相应类型的序列化和反序列化
好处:只定义一个序列化器就可以了(通用)
这里我们也做个测试,分别用不修改ObjectMapper的和修改了ObjectMapper的看看生成的value有啥子不一样
运行结果:
ob1: [“com.hcy.core.model.UserDO”,{“userid”:null,“openid”:null,“name”:“name”,“city”:“city”}]
ob2: {“userid”:null,“openid”:null,“name”:“name”,“city”:“city”}
这里结果很明显啦!!!
希望对大家有帮助!!!
以上这篇使用Redis获取数据转json,解决动态泛型传参的问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持猪先飞。
相关文章
- 这篇文章主要介绍了gin 获取post请求的json body操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-15
- 这篇文章主要介绍了详解如何清理redis集群的所有数据,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-18
- 这篇文章主要介绍了Redis连接池配置及初始化实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-29
- 新版 Mysql 中加入了对 JSON Document 的支持,可以创建 JSON 类型的字段,并有一套函数支持对JSON的查询、修改等操作,下面就实际体验一下...2016-08-23
- JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。JSON采用完全独立于语言的文本格式,这些特性使JSON成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成...2021-11-05
C#使用Http Post方式传递Json数据字符串调用Web Service
这篇文章主要为大家详细介绍了C#使用Http Post方式传递Json数据字符串调用Web Service,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-25详解redis desktop manager安装及连接方式
这篇文章主要介绍了redis desktop manager安装及连接方式,本文图文并茂给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下...2021-01-15- 这篇文章主要介绍了浅谈redis key值内存消耗以及性能影响,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-07
- 下面小编就为大家带来一篇js遍历json的key和value的实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2017-01-26
- 最近在工作中遇到了一个问题,通过查找相关资料才得知原因是因为返回结果的问题,下面这篇文章主要给大家介绍了关于lua读取redis数据的null判断的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下...2020-06-30
- 这篇文章主要介绍了SpringBoot集成Redis实现消息队列的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-10
- 这篇文章主要介绍了详解.NET Core 3.0 里新的JSON API,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
redis setIfAbsent和setnx的区别与使用说明
这篇文章主要介绍了redis setIfAbsent和setnx的区别与使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-08-04- 这篇文章主要介绍了Redis的Expire与Setex区别说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-01-15
ASP.NET Core根据环境变量支持多个 appsettings.json配置文件
这篇文章主要介绍了ASP.NET Core根据环境变量支持多个 appsettings.json配置文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22基于Vue+Openlayer实现动态加载geojson的方法
本文通过实例代码给大家介绍基于Vue+Openlayer实现动态加载geojson的方法,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧...2021-09-01- 这篇文章主要介绍了C#泛型类型知识,文中代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-06-25
- 这篇文章主要介绍了C#泛型的类型参数约束的相关资料,文中讲解非常细致,帮助大家更好的理解和学习c#,感兴趣的朋友可以了解下...2020-07-31
- 下面就为大家带来一篇jquery对Json的各种遍历方法总结(必看篇)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2016-10-02
解决HttpPost+json请求---服务器中文乱码及其他问题
这篇文章主要介绍了解决HttpPost+json请求---服务器中文乱码及其他问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-01-22