使用Redis获取数据转json,解决动态泛型传参的问题_Redis

来源:脚本之家  责任编辑:小易  

redis、memcached只能存字符串,你要把你的数据序列化成字符串,才可以存进去,取出来再反序列化www.zgxue.com防采集请勿采集本网。

场景:

项目有两种角色需要不同的登录权限,将redis做为用户登录信息缓存数据库。码一个方法,希望能够根据传入不用用户实体类型来获取相应的数据。用户实体为:SessionEntity<User1>、SessionEntity<User2>。json使用FastJson。

redis、memcached只能存字符串,要把数据序列化成字符串,才可以存进去,取出来再反序列化。

先阐述遇到的几个问题:

3.用redis存多维数组,可以把数组json_encode转换成json各式数据,以string类型的方式存储。读取的时候再json_decode回来。4.Redis本身不支持存取PHP数组的数据结构,但是如何存取PHP的数组呢?可以把数组

1、redis获取到的数据序列化后,转json,经常提示转换异常(并不是每次,只是时常)。

把要存的数组序列化 或者 json_encode后 变成字符串再存。取的时候 反序列号或者json_decode处理成数组。

2、不想每种用户都书写一个redis操作方法(显得tai low)。

估计是百度开发云的redis在在线测试,包装了redis客户端,就提供字符存储,不然的话,存储字符串,字节数组,或是其它类型,是不会出问题的。你在看看他的文档里面关于redis的说明。

解决:

1、redis获取到的数据序列化后,转json,经常提示转换异常:

如果是数据完全可以使用JSON方式。毕竟redis直接set、get使用起 来门槛低很多。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; }

测试代码为

@Testpublic 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.UserDOat 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,解决动态泛型传参的问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持真格学网。

Redis-Dump是一个将Redis数据导入/导出为json格式数据的小工具,目前还在Alpha版本,目前提供两个命令,将Redis数据层出成json的redis-dump命令,以及将json文件导入到redis中的redis-load命令,喜欢尝鲜的同学可以试玩一下。另外个人认为这也是一个适合新手的Redis数据结构学习工具,通过将Redis几种数据结构dump成json,可以让我们更直观的认识Redis数据结构及其操作内容来自www.zgxue.com请勿采集。


  • 本文相关:
  • 基于redis实现token验证用户是否登陆
  • macos上redis的安装与测试操作
  • redis集群下过期key监听的实现代码
  • 详解redis使用认证密码登录
  • 基于redis实现分布式锁以及任务队列
  • redis入门教程_动力节点java学院整理
  • 基于redis分布式锁实现秒杀功能
  • 分割超大redis数据库例子
  • redis配置文件详解
  • redis教程(一):redis简介
  • centos 6.6下redis安装配置记录
  • redis 对比 memcached 并在 centos 下进行安装配置详解
  • redis命令查询出数据能转成json格式显示吗
  • 如何用json向redis存数据
  • 用JSON 和Redis建立数据模型 求例子和讲解JSON的和Redis的模型都要
  • 怎样讲一个json数据用rset命令存入redis
  • redis怎么存储json数据
  • redis怎么存数组和获取数组?
  • redis 怎么存数组和获取数组
  • redis怎么存储json数据
  • redis 存储java对象是json字符串还是序列化
  • 我在用,能把读取redis的结果封装为json吗
  • 网站首页网页制作脚本下载服务器操作系统网站运营平面设计媒体动画电脑基础硬件教程网络安全mssqlmysqlmariadboracledb2mssql2008mssql2005sqlitepostgresqlmongodbredisaccess数据库文摘数据库其它首页基于redis实现token验证用户是否登陆macos上redis的安装与测试操作redis集群下过期key监听的实现代码详解redis使用认证密码登录基于redis实现分布式锁以及任务队列redis入门教程_动力节点java学院整理基于redis分布式锁实现秒杀功能分割超大redis数据库例子redis配置文件详解redis教程(一):redis简介centos 6.6下redis安装配置记录redis 对比 memcached 并在 centos 下进行安装配置详解超强、超详细redis数据库入门教程redis常用命令、常见错误、配置技redis操作命令总结redis中5种数据结构的使用场景介64位windows下安装redis教程redis中使用redis-dump导出、导入redis中统计各种数据大小的方法redis常用命令小结让redis在你的系统中发挥更大作用centos 6.6下redis安装配置记录redis主从复制问题和扩容问题的解决思路redis获取某个大key值的脚本实例大家都应该知道的redis过期键与过期策略windows下redis的安装使用图解redis通过pipeline提升吞吐量的方法通过 redis 实现 rpc 远程方法调用(支持多基于redis实现分布式锁的原理与方法ubuntu 16.04安装redis的两种方式教程详解redis中如何使用lua脚本让你的灵活性提高安装redis就那么几步,很简单
    免责声明 - 关于我们 - 联系我们 - 广告联系 - 友情链接 - 帮助中心 - 频道导航
    Copyright © 2017 www.zgxue.com All Rights Reserved