首页>>后端>>SpringBoot->SpringBoot整合reids之JSON序列化文件夹操作

SpringBoot整合reids之JSON序列化文件夹操作

时间:2023-11-30 本站 点击:1

前言

最近在开发项目,用到了Redis作为缓存,来提高系统访问速度和缓解系统压力,提高用户响应和访问速度,这里遇到几个问题做一下总结和整理

快速配置

SpringBoot整合redis有专门的场景启动器整合起来还是非常方便的

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

如果使用redis连接池引入

<!--redis连接池--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency>

集成配置文件

#------------------redis缓存配置------------#Redis数据库索引(默认为0)spring.redis.database=1#Redis服务器地址spring.redis.host=127.0.0.1#Redis服务器连接端口spring.redis.port=6379#Redis密码spring.redis.password#连接超时时间(毫秒)spring.redis.timeout=5000#redis连接池#连接池中的最小空闲连接spring.redis.lettuce.pool.min-idle=10#连接池中的最大空闲连接spring.redis.lettuce.pool.max-idle=500#连接池最大连接数(使用负值表示没有限制)spring.redis.lettuce.pool.max-active=2000#连接池最大阻塞等待时间(使用负值表示没有限制)spring.redis.lettuce.pool.max-wait=10000

JSON序列化

由于缓存数据默认使用的是jdk自带的序列化 二进制 需要序列化的实体类继承Serializable接口。而且序列化后的内容在redis中看起来也不是很方便。

\xAC\xED\x00\x05sr\x00Lorg.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken/\xDFGc\x9D\xD0\xC9\xB7\x02\x00\x01L\x00\x0Aexpirationt\x00\x10Ljava/util/Date;xr\x00Dorg.springframework.security.oauth2.common.DefaultOAuth2RefreshTokens\xE1\x0E\x0AcT\xD4^\x02\x00\x01L\x00\x05valuet\x00\x12Ljava/lang/String;xpt\x00$805a75f7-2ee2-4a27-a598-591bfa1cf17dsr\x00\x0Ejava.util.Datehj\x81\x01KYt\x19\x03\x00\x00xpw\x08\x00\x00\x01}y\x81\xDB\x9Ax

于是萌生了需要将数据序列化成json的想法。

jackson序列化

在使用spring-data-redis,默认情况下是使用org.springframework.data.redis.serializer.JdkSerializationRedisSerializer这个类来做序列化,Jackson redis序列化是spring中自带的.我们使用jackson方式

@Bean@ConditionalOnClass(RedisOperations.class)publicRedisTemplate<String,Object>redisTemplate(RedisConnectionFactoryfactory){RedisTemplate<String,Object>template=newRedisTemplate<>();template.setConnectionFactory(factory);Jackson2JsonRedisSerializer<Object>jackson2JsonRedisSerializer=newJackson2JsonRedisSerializer<>(Object.class);//序列化包括类型描述否则反向序列化实体会报错,一律都为JsonObjectObjectMappermapper=newObjectMapper();mapper.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(),ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(mapper);StringRedisSerializerstringRedisSerializer=newStringRedisSerializer();//key采用String的序列化方式template.setKeySerializer(stringRedisSerializer);//hash的key也采用String的序列化方式template.setHashKeySerializer(stringRedisSerializer);//value序列化方式采用jacksontemplate.setValueSerializer(jackson2JsonRedisSerializer);//hash的value序列化方式采用jacksontemplate.setHashValueSerializer(jackson2JsonRedisSerializer);template.afterPropertiesSet();returntemplate;}

序列化后存储在redis后内容

["com.qhong.test.dependBean.Person",{"age":20,"name":"name0","iss":true}]

["java.util.ArrayList",[["com.qhong.test.dependBean.Person",{"age":20,"name":"name0","iss":true}],["com.qhong.test.dependBean.Person",{"age":21,"name":"name1","iss":true}],["com.qhong.test.dependBean.Person",{"age":22,"name":"name2","iss":true}]]]

上面的不是严格符合json格式规范,虽然比默认二进制好

注意这里序列化json代类型 "com.qhong.test.dependBean.Person" 如果没有这个反序列化会报类型转换异常错误

也就是代码中这一段必须设置,我之前就是没有设置,反序列化都是JsonObject必须自己转换类型,否则会报错

//序列化包括类型描述否则反向序列化实体会报错,一律都为JsonObjectObjectMappermapper=newObjectMapper();mapper.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(),ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(mapper);

Fastjson序列化

需要倒入Fastjson到依赖

<!--JSON工具--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.76</version></dependency>

实现RedisSerializer接口

importcom.alibaba.fastjson.JSON;importcom.alibaba.fastjson.parser.ParserConfig;importcom.alibaba.fastjson.serializer.SerializerFeature;importorg.springframework.data.redis.serializer.RedisSerializer;importorg.springframework.data.redis.serializer.SerializationException;

import java.nio.charset.Charset; import java.nio.charset.StandardCharsets;

public class FastJson2JsonRedisSerializerimplements RedisSerializer{

<!--redis连接池--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency>0

}

<!--redis连接池--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency>1

注意这是一种方式自己实现RedisSerializer 序列化接口 但是FastJson 1.2.36版本以后不需要自己实现RedisSerializer

为我们提供序列化支持在com.alibaba.fastjson.support.spring中 有GenericFastJsonRedisSerializerFastJsonRedisSerializer 两个实现类 ,

区别在于GenericFastJsonRedisSerializer 可以自动转换对象类型,FastJsonRedisSerializer需要自定义转换需要的类型。

通常使用 GenericFastJsonRedisSerializer 即可满足大部分场景,如果你想定义特定类型专用的 RedisTemplate 可以使用 FastJsonRedisSerializer 来代替 GenericFastJsonRedisSerializer”

FastJson github有对应问题描述lssues 我已入坑 ,刚开始一直使用FastJsonRedisSerializer****无法自动反向序列化

序列化后存储在redis后内容

<!--redis连接池--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency>2

<!--redis连接池--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency>3

正常情况是格式是正确的,但是如果你存储内容出现set或者doubble类型,会带上Set,D类型描述如下

会出现问题无法解析,但是在程序里是可以反向序列化的

分析参考对比

jdkSerializationRedisSerializer: 使用JDK提供的序列化功能。 优点是反序列化时不需要提供类型信息(class),但缺点是需要实现Serializable接口,还有序列化后的结果非常庞大,是JSON格式的5倍左右,这样就会消耗redis服务器的大量内存。

Jackson2JsonRedisSerializer: 使用Jackson库将对象序列化为JSON字符串。优点是速度快,序列化后的字符串短小精悍,不需要实现Serializable接口。但缺点也非常致命,那就是此类的构造函数中有一个类型参数,必须提供要序列化对象的类型信息(.class对象)。 通过查看源代码,发现其只在反序列化过程中用到了类型信息。

FastJsonRedisSerializer性能最优号称最快的json解析库,但是反序列化后类字段顺序和原来实体类不一致发生改变,在某些set,double字段情况下json格式不正确,但是在程序可以解析

更多问题参考

RedisTemplate序列化方式解读

redis数据库操作

在整合了spring-boot-starter-data-redis后会自动帮我们注入redisTemplate 对象,专门用来操作reids数据库的

在reids中如果想用文件夹方式存储key的话类似这样

我们只需要在存储使用使用::表示文件夹就可以了

<!--redis连接池--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency>4

如果需要模糊查询key话使用*来表示 如

获取所有key

<!--redis连接池--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency>5

模糊批量删除

<!--redis连接池--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency>6

<!--redis连接池--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency>7

因为使用很频繁所以我写成工具库RedisUtil 通过静态方法方式去调用就可以了

基本上包含工作中用到的所有方法, 这里附上源码

packagecn.soboys.kmall.cache.utils;importcn.hutool.extra.spring.SpringUtil;importorg.springframework.data.redis.core.RedisTemplate;importjava.util.Arrays;importjava.util.List;importjava.util.Map;importjava.util.Set;importjava.util.concurrent.TimeUnit;/***定义常用的Redis操作**@authorkenx*/publicclassRedisUtil{privatestaticfinalRedisTemplate<String,Object>redisTemplate=SpringUtil.getBean("redisTemplate",RedisTemplate.class);/***指定缓存失效时间**@paramkey键*@paramtime时间(秒)*@returnBoolean*/publicstaticBooleanexpire(Stringkey,Longtime){try{if(time>0){redisTemplate.expire(key,time,TimeUnit.SECONDS);}returntrue;}catch(Exceptione){e.printStackTrace();returnfalse;}}/***根据key获取过期时间**@paramkey键不能为null*@return时间(秒)返回0代表为永久有效*/publicstaticLonggetExpire(Stringkey){returnredisTemplate.getExpire(key,TimeUnit.SECONDS);}/***判断key是否存在**@paramkey键*@returntrue存在false不存在*/publicstaticBooleanhasKey(Stringkey){try{returnredisTemplate.hasKey(key);}catch(Exceptione){e.printStackTrace();returnfalse;}}/***删除缓存**@paramkey可以传一个值或多个*/publicstaticvoiddel(String...key){if(key!=null&&key.length>0){if(key.length==1){redisTemplate.delete(key[0]);}else{redisTemplate.delete(Arrays.asList(key));}}}<!--redis连接池--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency>7/***普通缓存获取**@paramkey键*@return值*/publicstaticObjectget(Stringkey){returnkey==null?null:redisTemplate.opsForValue().get(key);}/***普通缓存放入**@paramkey键*@paramvalue值*@returntrue成功false失败*/publicstaticBooleanset(Stringkey,Objectvalue){try{redisTemplate.opsForValue().set(key,value);returntrue;}catch(Exceptione){e.printStackTrace();returnfalse;}}/***普通缓存放入并设置时间**@paramkey键*@paramvalue值*@paramtime时间(秒)time要大于0如果time小于等于0将设置无限期*@returntrue成功false失败*/publicstaticBooleanset(Stringkey,Objectvalue,Longtime){try{if(time>0){redisTemplate.opsForValue().set(key,value,time,TimeUnit.SECONDS);}else{set(key,value);}returntrue;}catch(Exceptione){e.printStackTrace();returnfalse;}}/***递增**@paramkey键*@paramdelta要增加几(大于0)*@returnLong*/publicstaticLongincr(Stringkey,Longdelta){if(delta<0){thrownewRuntimeException("递增因子必须大于0");}returnredisTemplate.opsForValue().increment(key,delta);}/***递减**@paramkey键*@paramdelta要减少几*@returnLong*/publicstaticLongdecr(Stringkey,Longdelta){if(delta<0){thrownewRuntimeException("递减因子必须大于0");}returnredisTemplate.opsForValue().increment(key,-delta);}/***HashGet**@paramkey键不能为null*@paramitem项不能为null*@return值*/publicstaticObjecthget(Stringkey,Stringitem){returnredisTemplate.opsForHash().get(key,item);}/***获取hashKey对应的所有键值**@paramkey键*@return对应的多个键值*/publicstaticMap<Object,Object>hmget(Stringkey){returnredisTemplate.opsForHash().entries(key);}/***获取hashKey对应的所有键**@paramkey键*@return对应的多个键*/publicstaticSet<String>hmgetKey(Stringkey){Mapmap=redisTemplate.opsForHash().entries(key);returnmap.keySet();}/***HashSet**@paramkey键*@parammap对应多个键值*@returntrue成功false失败*/publicstaticBooleanhmset(Stringkey,Map<String,Object>map){try{redisTemplate.opsForHash().putAll(key,map);returntrue;}catch(Exceptione){e.printStackTrace();returnfalse;}}/***HashSet并设置时间**@paramkey键*@parammap对应多个键值*@paramtime时间(秒)*@returntrue成功false失败*/publicstaticBooleanhmset(Stringkey,Map<String,Object>map,Longtime){try{redisTemplate.opsForHash().putAll(key,map);if(time>0){expire(key,time);}returntrue;}catch(Exceptione){e.printStackTrace();returnfalse;}}/***向一张hash表中放入数据,如果不存在将创建**@paramkey键*@paramitem项*@paramvalue值*@returntrue成功false失败*/publicstaticBooleanhset(Stringkey,Stringitem,Objectvalue){try{redisTemplate.opsForHash().put(key,item,value);returntrue;}catch(Exceptione){e.printStackTrace();returnfalse;}}/***向一张hash表中放入数据,如果不存在将创建**@paramkey键*@paramitem项*@paramvalue值*@paramtime时间(秒)注意:如果已存在的hash表有时间,这里将会替换原有的时间*@returntrue成功false失败*/publicstaticBooleanhset(Stringkey,Stringitem,Objectvalue,Longtime){try{redisTemplate.opsForHash().put(key,item,value);if(time>0){expire(key,time);}returntrue;}catch(Exceptione){e.printStackTrace();returnfalse;}}/***删除hash表中的值**@paramkey键不能为null*@paramitem项可以使多个不能为null*/publicstaticvoidhdel(Stringkey,Object...item){redisTemplate.opsForHash().delete(key,item);}/***判断hash表中是否有该项的值**@paramkey键不能为null*@paramitem项不能为null*@returntrue存在false不存在*/publicstaticBooleanhHasKey(Stringkey,Stringitem){returnredisTemplate.opsForHash().hasKey(key,item);}/***hash递增如果不存在,就会创建一个并把新增后的值返回**@paramkey键*@paramitem项*@paramby要增加几(大于0)*@returnDouble*/publicstaticDoublehincr(Stringkey,Stringitem,Doubleby){returnredisTemplate.opsForHash().increment(key,item,by);}/***hash递减**@paramkey键*@paramitem项*@paramby要减少记(小于0)*@returnDouble*/publicstaticDoublehdecr(Stringkey,Stringitem,Doubleby){returnredisTemplate.opsForHash().increment(key,item,-by);}/***根据key获取Set中的所有值**@paramkey键*@returnSet*/publicstaticSet<Object>sGet(Stringkey){try{returnredisTemplate.opsForSet().members(key);}catch(Exceptione){e.printStackTrace();returnnull;}}/***根据value从一个set中查询,是否存在**@paramkey键*@paramvalue值*@returntrue存在false不存在*/publicstaticBooleansHasKey(Stringkey,Objectvalue){try{returnredisTemplate.opsForSet().isMember(key,value);}catch(Exceptione){e.printStackTrace();returnfalse;}}/***将数据放入set缓存**@paramkey键*@paramvalues值可以是多个*@return成功个数*/publicstaticLongsSet(Stringkey,Object...values){try{returnredisTemplate.opsForSet().add(key,values);}catch(Exceptione){e.printStackTrace();return0L;}}/***将set数据放入缓存**@paramkey键*@paramtime时间(秒)*@paramvalues值可以是多个*@return成功个数*/publicstaticLongsSetAndTime(Stringkey,Longtime,Object...values){try{Longcount=redisTemplate.opsForSet().add(key,values);if(time>0){expire(key,time);}returncount;}catch(Exceptione){e.printStackTrace();return0L;}}/***获取set缓存的长度**@paramkey键*@returnLong*/publicstaticLongsGetSetSize(Stringkey){try{returnredisTemplate.opsForSet().size(key);}catch(Exceptione){e.printStackTrace();return0L;}}/***移除值为value的**@paramkey键*@paramvalues值可以是多个*@return移除的个数*/publicstaticLongsetRemove(Stringkey,Object...values){try{returnredisTemplate.opsForSet().remove(key,values);}catch(Exceptione){e.printStackTrace();return0L;}}/***获取list缓存的内容**@paramkey键*@paramstart开始*@paramend结束0到-1代表所有值*@returnList*/publicstaticList<Object>lGet(Stringkey,Longstart,Longend){try{returnredisTemplate.opsForList().range(key,start,end);}catch(Exceptione){e.printStackTrace();returnnull;}}/***获取list缓存的长度**@paramkey键*@returnLong*/publicstaticLonglGetListSize(Stringkey){try{returnredisTemplate.opsForList().size(key);}catch(Exceptione){e.printStackTrace();return0L;}}/***通过索引获取list中的值**@paramkey键*@paramindex索引index>=0时,0表头,1第二个元素,依次类推;*index<0时,-1,表尾,-2倒数第二个元素,依次类推*@returnObject*/publicstaticObjectlGetIndex(Stringkey,Longindex){try{returnredisTemplate.opsForList().index(key,index);}catch(Exceptione){e.printStackTrace();returnnull;}}/***将list放入缓存**@paramkey键*@paramvalue值*@returnBoolean*/publicstaticBooleanlSet(Stringkey,Objectvalue){try{redisTemplate.opsForList().rightPush(key,value);returntrue;}catch(Exceptione){e.printStackTrace();returnfalse;}}/***将list放入缓存**@paramkey键*@paramvalue值*@paramtime时间(秒)*@returnBoolean*/publicstaticBooleanlSet(Stringkey,Objectvalue,Longtime){try{redisTemplate.opsForList().rightPush(key,value);if(time>0){expire(key,time);}returntrue;}catch(Exceptione){e.printStackTrace();returnfalse;}}/***将list放入缓存**@paramkey键*@paramvalue值*@returnBoolean*/publicstaticBooleanlSet(Stringkey,List<Object>value){try{redisTemplate.opsForList().rightPushAll(key,value);returntrue;}catch(Exceptione){e.printStackTrace();returnfalse;}}/***将list放入缓存**@paramkey键*@paramvalue值*@paramtime时间(秒)*@returnBoolean*/publicstaticBooleanlSet(Stringkey,List<Object>value,Longtime){try{redisTemplate.opsForList().rightPushAll(key,value);if(time>0){expire(key,time);}returntrue;}catch(Exceptione){e.printStackTrace();returnfalse;}}/***根据索引修改list中的某条数据**@paramkey键*@paramindex索引*@paramvalue值*@returnBoolean*/publicstaticBooleanlUpdateIndex(Stringkey,Longindex,Objectvalue){try{redisTemplate.opsForList().set(key,index,value);returntrue;}catch(Exceptione){e.printStackTrace();returnfalse;}}/***移除N个值为value**@paramkey键*@paramcount移除多少个*@paramvalue值*@return移除的个数*/publicstaticLonglRemove(Stringkey,Longcount,Objectvalue){try{returnredisTemplate.opsForList().remove(key,count,value);}catch(Exceptione){e.printStackTrace();return0L;}}publicstaticSet<String>getAllKey(Stringkeys){Set<String>key=redisTemplate.keys(keys+"*");returnkey;}}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/SpringBoot/4348.html