SpringBoot集成Redis及Redis使用方法
作者:李指导、
应用背景
将一些经常展现和不会频繁变更的数据,存放在存取速率更快的地方。 缓存就是一个存储器,在技术选型中,常用 Redis 作为缓存数据库,可以帮我们分散掉数据库的压力,有了它能更好的支持并发性能,主要是在获取资源方便性能优化的关键方面。可以这样理解redis位于数据库和springboot框架之间,起到数据缓存的作用。
Redis简介
- Redis介绍:Redis是现在最受欢迎的NoSQL数据库之一,Redis是一个使用ANSI C编写的开源、包含多种数据结构、支持网络、基于内存、可选持久性的键值对存储数据库。
- Redis使用场景:缓存系统(“热点”数据:高频读、低频写)、计数器、消息队列系统、排行榜、社交网络和实时系统
- Redis数据类型:Redis提供的数据类型主要分为5种自有类型和一种自定义类型,这5种自有类型包括:String类型、哈希类型、列表类型、集合类型和顺序集合类型。
更新缓存模式 Cache aside这是最常用最常用的pattern了。其具体逻辑如下:
- 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
- 命中:应用程序从cache中取数据,取到后返回。
- 更新:先把数据存到数据库中,成功后,再让缓存失效。
更新问题
- 我们知道,在 springboot 1.5.x版本的默认的Redis客户端是 Jedis实现的,需要导入jedis依赖,而springboot 2.x版本中默认客户端是用 lettuce实现的,需要导入spring-boot-starter-data-redis依赖。这两种方式使用的都是 TCP协议。可以理解为:咱们通过程序是不能直接连接 Redis,得利用客户端工具才能进行连接。比较常用的有两种:Jedis、Lettuce。既然 Lettuce 和 Jedis 的都是连接 Redis Server 的客户端,那么它们有什么区别呢?
- Jedis使用直连方式连接Redis Server,在多线程环境下存在线程安全问题, 因此需要增加连接池来解决线程安全的问题,同时可以限制redis客户端的数量, 但这种直连方式基于传统I/O模式,是阻塞式传输。
- 而 Lettuce 是 一种可伸缩,线程安全,完全非阻塞的Redis客户端,底层基于netty通信,我们知道netty是基于NIO的非阻塞通信, 天生支持高并发,因此在在多线程环境下不存在线程安全问题,一个连接实例就可以满足多线程环境下的并发访问, 当然实例不够的情况下也可以按需增加实例,保证伸缩性。
- 下面我们通过源码的方式解析springboot是如何通过lettuce方式连接redis server的,以及springboot操作redis的底层原理。
一:环境配置
1.1: 在pom.xml文件中添加依赖
这里说说为什么要添加 org.apache.commons 依赖,如果不加,它会报错:Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool2.impl.GenericObjectPoolConfig
<dependencies> <!-- SpringBoot集成Redis的起步依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>1.4.7.RELEASE</version> </dependency> <!--lettuce 依赖commons-pool--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.8.0</version> </dependency> </dependencies>
1.2:配置SpringBoot核心配置文件application.properties
yml文件格式
spring: redis: open: true # 是否开启redis缓存 true开启 false关闭 database: 0 host: 127.0.0.1 port: 3304 password: 123456 # 密码(默认为空) timeout: 6000ms # 连接超时时长(毫秒) expire: 3600 #7天不过期 lettuce: pool: max-active: 100 # 连接池最大连接数(使用负值表示没有限制) max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制) max-idle: 20 # 连接池中的最大空闲连接 min-idle: 5 # 连接池中的最小空闲连接
properties文件格式
#Redis ##Redis数据库索引 spring.redis.database=0 ##Redis服务器地址 spring.redis.host=127.0.0.1 ## Redis服务器连接端口 spring.redis.port=3304 ## 连接超时时间(毫秒) spring.redis.timeout=3 ## Redis服务器连接密码(默认为空) spring.redis.password=135246 ## 连接池中的最大连接数 (使用复数则标识没有限制) 默认 8 spring.redis.pool.max.active=100 ## 连接池最大阻塞等待时间(使用负值表示没有限制)默认 -1 spring.redis.pool.max.wait=-1 ## 连接池中的最大空闲连接 默认 8 spring.redis.pool.max.idle=20 ## 连接池中的最小空闲连接 默认 0 spring.redis.pool.max.idle=0
二:在Config文件夹中创建RedisConfig配置文件类
RedisTemplate 是 Spring 操作 Redis 的重点内容。 RedisTemplate是一个强大的类,首先它会自动从 RedisConnectionFactory 工厂中获取连接,然后执行对应的 Redis命令,提供了redis各种操作、异常处理及序列化,支持发布订阅,并对spring 3.1 cache进行了实现,在最后还会关闭 Redis 的连接。
2.1:RedisTemplate中的几个角色:
- RedisSerializer:由于与Redis服务器的通信一定是使用字节数组完成的,所以RedisSerializer是将Java对象编码解码的组件
- RedisOperations:封装了一些Redis操作
- XXXOperations:封装了指定类型或功能的数据的操作,如ZSetOperations
2.2:为什么要自定义序列化:
RedisTemplate操作时,默认会采用jdkSerializable序列化机制,使得插入的值在redis客户端看来会有乱码 类似于: "\xac\ced\x00\x05t\x00\x03key" ,所以解决这个问题就需要修改默认的序列化规则。
2.2.1:Spring 中提供了以下几个序列化器:
- Jackson2JsonRedisSerializer
- JdkSerializationRedisSerializer
- OxmSerializer
- StringRedisSerializer
- GenericToStringRedisSerializer
- GenericJackson2JsonRedisSerializer
本章使用的是StringRedisSerializer, String序列化方式。
RedisConfig 所在结构地址:
package com.lizexin.springbootdemo.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.*; import org.springframework.data.redis.serializer.StringRedisSerializer; /** * 项目名称:springboot-demo * 类名称:RedisConfig * 类描述:Redis配置 * 创建时间:2023/08/04 * @author lzx * @version v1.0 */ @Configuration public class RedisConfig { @Autowired private RedisConnectionFactory factory; @Bean public RedisTemplate<String, Object> redisTemplate() { // 将template 泛型设置为 <String, Object> RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); // 使用 String 序列化方式,序列化 KEY。 redisTemplate.setKeySerializer(new StringRedisSerializer()); // 使用 String 序列化方式,序列化 VALUE。 redisTemplate.setValueSerializer(new StringRedisSerializer()); // 使用 String 序列化方式,序列化 HashKEY。 redisTemplate.setHashKeySerializer(new StringRedisSerializer()); // 使用 String 序列化方式,序列化 ValueKEY。 redisTemplate.setHashValueSerializer(new StringRedisSerializer()); // 配置连接工厂 redisTemplate.setConnectionFactory(factory); return redisTemplate; } /** * HashOperations * 操作 Hash 类型数据 **/ @Bean public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForHash(); } /** * HashOperations * 操作 String 类型数据 **/ @Bean public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) { return redisTemplate.opsForValue(); } /** * HashOperations * 操作 List 类型数据 **/ @Bean public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForList(); } /** * HashOperations * 操作 Set 类型数据 **/ @Bean public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForSet(); } /** * HashOperations * 操作 SortedSet 类型数据 **/ @Bean public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForZSet(); } }
四:封装Redis Utils工具包
Redis工具包分为三个类
1:RedisUtils.java Redis方法类主要记录对redis的一些操作,增删改查等。
2:RedisKeys.java Redis自定义Key类,自定义配置,对redis操作时好分辨哪个key的数据
3:UserRedis.java 封装类,将RedisUtils和RedisKey进行封装,用户直接操作此类
redis 工具包 所在结构地址:
4.1:RedisUtils.java
package com.lizexin.springbootdemo.utils.redis; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.*; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; /** * 项目名称:springboot-demo * 类名称:RedisUtils * 类描述:Redis工具类 * 创建时间:2023/08/04 * @author lzx * @version v1.0 */ @Component public class RedisUtils { /**日志*/ private static final Logger logger = LoggerFactory.getLogger(RedisUtils.class); @Autowired private RedisTemplate<String, Object> redisTemplate; @Autowired private ValueOperations<String, String> valueOperations; @Autowired private HashOperations<String, String, Object> hashOperations; @Autowired private ListOperations<String, Object> listOperations; @Autowired private SetOperations<String, Object> setOperations; @Autowired private ZSetOperations<String, Object> zSetOperations; /**默认过期时长,单位: 秒*/ public final static long DEFAULT_EXPIRE = 60 * 10; /**从配置文件获取 默认过期时长*/ @Value("${spring.redis.expire}") public long expire; /**不设置过期时长 */ public final static long NOT_EXPIRE = -1; /**它可以帮助我们快速的进行各个类型和Json类型的相互转换*/ private static final ObjectMapper MAPPER = new ObjectMapper(); /**给指定key设置固定时间的有效期*/ public void expireAt(String key,Date date){ redisTemplate.expireAt(key,date); } /**根据指定的key,获取过期时间*/ public long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS);} /**判断key是否存在*/ public boolean hasKey(String key) { try { return redisTemplate.hasKey(key); } catch (Exception e) { e.printStackTrace(); return false; } } /** * 删除缓存, @param key可以传一个值 或多个 * 该注解屏蔽某些编译时的警告信息 * */ @SuppressWarnings("unchecked") public void delete(String... key) { if (key != null && key.length > 0) { if (key.length == 1) { redisTemplate.delete(key[0]); } else { redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key)); } } } /**将Object值放如缓存并设置默认时间,调用下一个方法将值转为JSON字符串*/ public void set(String key, Object value){ set(key, value, DEFAULT_EXPIRE); } /**将Object值转为JSON字符串放入缓存,并设置过期时长*/ public void set(String key, Object value, long expire){ valueOperations.set(key, objectToJson(value)); if(expire != NOT_EXPIRE){ redisTemplate.expire(key, expire, TimeUnit.SECONDS); } } /**根据key和泛型获取值, 调用下一个get方法*/ public <T> T get(String key, Class<T> clazz) { return get(key, clazz, NOT_EXPIRE); } /**根据key键获取值并转为对象 并重新设置过期时间*/ public <T> T get(String key, Class<T> clazz, long expire) { String value = valueOperations.get(key); if(expire != NOT_EXPIRE){ redisTemplate.expire(key, expire, TimeUnit.SECONDS); } //将Json字符串转换为bean对象 return value == null ? null : fromJson(value, clazz); } /**根据key键获取值返回为String*/ public String get(String key, long expire) { String value = valueOperations.get(key); if(expire != NOT_EXPIRE){ redisTemplate.expire(key, expire, TimeUnit.SECONDS); } return value; } /*根据key获取值*/ public String get(String key) { return get(key, NOT_EXPIRE); } /**Object转换为JSON字符串,在存reid的时候调用此方法*/ private String objectToJson(Object object){ if(object instanceof Integer || object instanceof Long || object instanceof Float || object instanceof Double || object instanceof Boolean || object instanceof String){ return String.valueOf(object); } return JSONUtil.toJsonStr(object); } /**JSON字符串, 转成javaBean对象*/ private <T> T fromJson(String json, Class<T> clazz){ return JSONUtil.toBean(json, clazz); //return JSON.parseObject(json,clazz); } /**将JsonObject转为实体类对象,转换异常将被抛出*/ public static <T> T fromJsonToBean(JSONObject json, Class<T> beanClass) { return null == json ? null : json.toBean(beanClass); } /**将元素添加到指定set集合中*/ public void addToSet(String key,String member){ redisTemplate.opsForSet().add(key,member); } /**批量添加到指定set集合中*/ public void addBatchToSet(String key,List<String> memberList) { Object[] members = memberList.toArray(); redisTemplate.opsForSet().add(key,members); } /**统计指定set的长度,当指定key的set不存在时,返回null*/ public Long countForSet(String key){ return redisTemplate.opsForSet().size(key); } /**只有当key不存在时才设置key的值,并返回true;当key存在时不修改key的值,并返回false。*/ public Boolean isMember(String value){ return setOperations.isMember(RedisKeys.AutoKey,value); } /**向集合添加值并设置过期时间*/ public Long addSetDataExpire(String value,String name,long expire){ Long addSet = setOperations.add(name,value); if(expire != NOT_EXPIRE){ redisTemplate.expire(name, expire, TimeUnit.SECONDS); } return addSet; } /**向右边批量添加元素*/ public boolean addrightPushAll(String key, List<Object> value) { boolean var4; try { this.redisTemplate.opsForList().rightPushAll(key, value); boolean var3 = true; return var3; } catch (Exception var8) { logger.error("", var8); var4 = false; } finally { this.close(); } return var4; } /** * 获取泛型的Collection Type * @param collectionClass 泛型的Collection * @param elementClasses 元素类 * @return JavaType Java类型 * @since 1.0*/ public static JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) { return MAPPER.getTypeFactory().constructParametricType(collectionClass, elementClasses); } private void close() { RedisConnectionUtils.unbindConnection(this.redisTemplate.getConnectionFactory()); } }
4.2:RedisKeys.java
package com.zhangtao.moguding.province.utils.redis; /** * 项目名称:user-center-service * 类名称:RedisKeys * 类描述:redis所有的key * 创建时间:2023/7/27 * * @author lzx * @version v1.0 */ public class RedisKeys { //最大蘑菇号的key public final static String MAX_MOGUNO_KEY = "moguding:user:max_mogu_no"; //短信验证码的key public static String getSmsCodeKey(String key,Integer type){ return "moguding:user:smsCode:" + key+":"+type; } //权限列表 public final static String PERMISSIONS_USERAUTH_KEY = "moguding:permissions:permissions_userauth_list:"; //参数配置 public static String getUserConfigKey(String... key){ return "moguding:user:config:" + key; } //用户Token public final static String AUTH_TOKEN_KEY = "moguding:user:authToken:"; //authtoken的key web端 public static String getAuthToken(String type,String userid){ if("web".equals(type)){ return AUTH_TOKEN_KEY+type+":" + userid; }else { return getAuthToken(userid); } } //authtoken的key app端 public static String getAuthToken(String userid){ return AUTH_TOKEN_KEY+ userid; } //缓存活跃蘑菇号key的 public final static String ACTIVE_MOGU_NO= "moguding:user:active:"; //缓存今日学校签到排行榜 public final static String SCHOOL_SIGN_RANK= "moguding:school:sign:rank"; //学校统计(实时发送【考勤,上岗,周报】) public final static String SCHOOL_COUNT= "moguding.school.count"; //自动报告key public final static String AutoKey = "autoReport_set"; //30天最后一次考勤 public final static String LastSign = "moguding.last.sign"; //省平台基础数据 public static String getProvinceConfigKey(String key){ return "moguding:province:config:" + key; } }
4.3:UserRedis.java
package com.zhangtao.moguding.province.utils.redis; import com.zhangtao.moguding.province.entity.UserConfigEntity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.List; /** * 项目名称:user-center-service * 类名称:UserRedis * 类描述:UserRedis * 创建时间:2019/5/27 * @author lzx * @version v1.0 */ /* *将RedisUtils的set方法和RedisKeys的key 封装到一起 * */ @Component public class UserRedis { @Autowired private RedisUtils redisUtils; public void set(String key,String value) { if(key == null){ return ; } String redisKey = RedisKeys.getUserConfigKey(key); redisUtils.set(redisKey, value,redisUtils.expire); } public void delete(String key) { if(key == null){ return ; } String redisKey = RedisKeys.getUserConfigKey(key); redisUtils.delete(redisKey); } public String get(String key){ if(key == null){ return null; } String redisKey = RedisKeys.getUserConfigKey(key); return redisUtils.get(redisKey); } public UserConfigEntity getObject(String key){ if(key == null){ return null; } String redisKey = RedisKeys.getUserConfigKey(key); return redisUtils.get(redisKey, UserConfigEntity.class); } //向Redis添加值,设置默认过期时长 7天, set方法将value进行序列化(转为JSON字符串) /* public void set(String key,Object value) { if(key == null){ return ; } String redisKey = RedisKeys.getRedisTestKey(key); redisUtils.set(redisKey, value,redisUtils.expire); } public <T> T get(String key, Class<T> clazz){ if(key == null){ return null; } String redisKey = RedisKeys.getRedisTestKey(key); return redisUtils.get(redisKey,clazz); } //判断Redis测试key是否存在 public boolean hasKey(String key){ if(key == null){ return false; } String redisKey = RedisKeys.getRedisTestKey(key); return redisUtils.hasKey(redisKey); };*/ //将今日活跃用户的蘑菇号批量存进redis指定Set集合中 public void addUserMoguNosToSet(List<String> moguNos){ redisUtils.addBatchToSet(RedisKeys.ACTIVE_MOGU_NO,moguNos); } //将今日活跃用户的蘑菇号缓存Set集合清空 public void deleteForCacheUserMoguNo(){ redisUtils.delete(RedisKeys.ACTIVE_MOGU_NO); } //判断Redis测试key是否存在 public boolean hasKey(String key){ if(key == null){ return false; } String redisKey = RedisKeys.getProvinceConfigKey(key); return redisUtils.hasKey(redisKey); }; //从redis中统计活跃用户数量 public int countActiveUser(){ Long Lnum = redisUtils.countForSet(RedisKeys.ACTIVE_MOGU_NO); if(Lnum==null){ return 0; }else { return Lnum.intValue(); } } //省级平台获取redisKey public String getProvinceKey(String key){ if(key == null){ return null; } String redisKey = RedisKeys.getProvinceConfigKey(key); return redisUtils.get(redisKey); } //省级平台向redis插入数据 public void setProvinceDataToRedis(String key,String list){ String keys = RedisKeys.getProvinceConfigKey(key); //配置文件默认保留时长 redisUtils.setRedis(keys,list,redisUtils.expire); } }
五:流程实现
模拟SpringBoot项目结构调用
1.RedisTestController
package com.lizexin.springbootdemo.Controller; import com.lizexin.springbootdemo.entity.Item; import com.lizexin.springbootdemo.service.RedisTestService; import com.zhangtao.common.dto.response.BaseResponse; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @program: springboot-demo * @author: lzx * @Time: 2023/8/9 16:35 * @description: Redis测试接口 * @version: v1.0 */ @RestController @Api(tags = {"Redis测试接口"},produces = "RedisTest_controller") @RequestMapping("/redis") public class RedisTestController { @Autowired RedisTestService redisTestService; private Logger logger = LoggerFactory.getLogger(RedisTestController.class); @ApiOperation(value = "Redis测试,将对象插入缓存",notes = "") @RequestMapping("/v1/test") public BaseResponse redisInsertBeanController(@RequestBody Item item){ return redisTestService.redisInsertBeanService(item); } @ApiOperation(value = "Redis测试,将List插入缓存",notes = "") @RequestMapping("/v2/test") public BaseResponse redisInsertListController(@RequestBody Item item){ return redisTestService.redisInsertListService(item); } @ApiOperation(value = "Redis测试,将Map<String,Set<String>>插入缓存,取出来转Map<String,JsonArry>",notes = "") @RequestMapping("/v3/test") public BaseResponse redisInsertMapController(@RequestBody Item item){ return redisTestService.redisInsertMapService(item); } }
2.RedisTestService
package com.lizexin.springbootdemo.service; import com.lizexin.springbootdemo.entity.Item; import com.zhangtao.common.dto.response.BaseResponse; /** * @program: springboot-demo * @author: lzx * @Time: 2023/8/9 22:55 * @description: Redis测试接口Service * @version: v1.0 */ public interface RedisTestService { BaseResponse redisInsertBeanService( Item item); BaseResponse redisInsertListService( Item item); BaseResponse redisInsertMapService( Item item); }
3.RedisTestServiceImpl
package com.lizexin.springbootdemo.service.impl; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSONArray; import com.lizexin.springbootdemo.entity.Item; import com.lizexin.springbootdemo.service.RedisTestService; import com.lizexin.springbootdemo.utils.*; import com.lizexin.springbootdemo.utils.redis.UserRedis; import com.zhangtao.common.dto.response.BaseResponse; import com.zhangtao.common.dto.response.ObjectResponse; import io.swagger.annotations.ApiOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.*; import java.util.stream.Collectors; /** * @program: springboot-demo * @author: lzx * @Time: 2023/08/9 22:58 * @description: test * @version: v1.0 */ @Service public class RedisTestServiceImpl implements RedisTestService { @Autowired UserRedis userRedis; private static final Logger logger = LoggerFactory.getLogger(RedisTestServiceImpl.class); @Override @ApiOperation( "通过key得到值并重新设置过期时间,若值不存在则重新插入缓存。"+ "set方法封装了 JSONUtil.toJsonStr"+ "get带泛型的方法封装了JSONUtil.toBean " ) public BaseResponse redisInsertBeanService(Item item) { String redisKey= "redisInsertBeanService"; System.out.println(redisKey); //判断key值是否存在,如果存在则优先取缓存 if (userRedis.hasKey(redisKey)){ Item jsonString= userRedis.get(redisKey,Item.class); logger.info("存在值"); logger.info(jsonString.toString()); return ObjectResponse.resObj(jsonString); }else{ //不存在则缓存 Item item1= AttributeData.list9(); logger.info("不存在值 插入"); userRedis.set(redisKey,item1); return ObjectResponse.ok(); } } @Override @ApiOperation("get方法不带泛型默认返回Json字符串,需要自行反序列化") public BaseResponse redisInsertListService(Item item) { //通过key得到值, String redisKey = "redisInsertListService"; System.out.println(redisKey); //判断key值是否存在,如果存在则优先取缓存 if (userRedis.hasKey(redisKey)){ List<Item> list = JSONArray.parseArray(userRedis.get(redisKey),Item.class); logger.info("存在值"); logger.info(list.toString()); return ObjectResponse.resObj(list); }else{ //不存在则缓存 List<Item> list= AttributeData.list8(); logger.info("不存在值 插入"); userRedis.set(redisKey,list); return ObjectResponse.ok(); } } @Override @ApiOperation("") public BaseResponse redisInsertMapService(Item item) { //通过key得到值, String redisKey= "redisInsertMapService"; System.out.println(redisKey); //判断key值是否存在,如果存在则优先取缓存 if (userRedis.hasKey(redisKey)){ String jsonString= userRedis.get(redisKey); //可以通过JSonString转对象方法把Vlue值从Set转为JsonArray Map<String,JSONArray> arrayMap= JSONUtil.toBean(jsonString,Map.class); logger.info("存在值"); logger.info(arrayMap.toString()); return ObjectResponse.resObj(arrayMap); }else{ //不存在则缓存 List<Item> list= AttributeData.list10(); //根据key转map,之后将Value换成set集合 //将集合添加至Map 指定参数作为key Map<String,List<Item>> map = new HashMap(); Map<String,Set<String>>setMap =new HashMap<>(); map = list.stream().collect(Collectors.groupingBy(Item::getName,Collectors.toList())); for (Map.Entry<String,List<Item>> key:map.entrySet()){ Set<String> set = new HashSet<>(); key.getValue().forEach(c->{ set.add(c.getValue()); }); setMap.put(key.getKey(), set); } logger.info("不存在值 插入"); userRedis.set(redisKey,setMap); return ObjectResponse.ok(); } } }
4.AttributeData
package com.lizexin.springbootdemo.utils; import com.lizexin.springbootdemo.dto.CommonInterfaceDto; import com.lizexin.springbootdemo.dto.InformationDatasDto; import com.lizexin.springbootdemo.dto.export.ExportGxySchoolFlowDto; import com.lizexin.springbootdemo.entity.Item; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @program: springboot-demo * @author: lzx * @Time: 2023/8/9 22:55 * @description: 常用数据集 * @version: v1.0 */ public class AttributeData { public static List<Map<String,Object>> list1 (){ //构建List集合1 List<Map<String,Object>> list1 = new ArrayList<>(); Map<String,Object> data=new HashMap<>(); data.put("userId","100001"); data.put("userName","唐僧"); list1.add(data); data=new HashMap<>(); data.put("userId","100002"); data.put("userName","八戒"); list1.add(data); data=new HashMap<>(); data.put("userId","100003"); data.put("userName","悟空"); list1.add(data); data=new HashMap<>(); data.put("userId","100004"); data.put("userName","沙僧"); list1.add(data); return list1; } public static List<Map<String,Object>> list2(){ Map<String,Object> data=new HashMap<>(); List<Map<String,Object>> list2 = new ArrayList<>(); data=new HashMap<>(); data.put("userId","100001"); data.put("gender","男"); data.put("age",20); list2.add(data); data=new HashMap<>(); data.put("userId","100002"); data.put("gender","雄"); data.put("age",1000); list2.add(data); data=new HashMap<>(); data.put("userId","100003"); data.put("gender","雄"); data.put("age",600); list2.add(data); data=new HashMap<>(); data.put("userId","100004"); data.put("gender","男"); data.put("age",800); list2.add(data); return list2; } public static List<InformationDatasDto> list3(){ List<InformationDatasDto> list = new ArrayList<>(); InformationDatasDto info = new InformationDatasDto(); info.setStudentId("10000"); info.setStudent_name("张三"); list.add(info); info = new InformationDatasDto(); info.setStudentId("10001"); info.setStudent_name("里李四"); list.add(info); info = new InformationDatasDto(); info.setStudentId("10002"); info.setStudent_name("王五"); list.add(info); info = new InformationDatasDto(); info.setStudentId("10003"); info.setStudent_name("赵六"); list.add(info); info = new InformationDatasDto(); info.setStudentId("10004"); info.setStudent_name("马七"); list.add(info); return list; } public static List<InformationDatasDto> list4(){ List<InformationDatasDto> list = new ArrayList<>(); InformationDatasDto info = new InformationDatasDto(); info.setStudentId("北京"); info.setStudent_name("张三"); list.add(info); info = new InformationDatasDto(); info.setStudentId("北京省"); info.setStudent_name("里李四"); list.add(info); info = new InformationDatasDto(); info.setStudentId("湖北省"); info.setStudent_name("王五"); list.add(info); info = new InformationDatasDto(); info.setStudentId("湖北"); info.setStudent_name("赵六"); list.add(info); info = new InformationDatasDto(); info.setStudentId("海南"); info.setStudent_name("马七"); list.add(info); return list; } public static List<ExportGxySchoolFlowDto> list5(){ List<ExportGxySchoolFlowDto> list = new ArrayList<>(); ExportGxySchoolFlowDto info = new ExportGxySchoolFlowDto(); info.setSchoolName("齐齐哈尔大学"); info.setDatas("黑龙江省"); info.setValue(10); list.add(info); info = new ExportGxySchoolFlowDto(); info.setSchoolName("齐齐哈尔大学"); info.setDatas("黑龙江"); info.setValue(20); list.add(info); info = new ExportGxySchoolFlowDto(); info.setSchoolName("齐齐哈尔大学"); info.setDatas("黑龙江省哈尔滨市"); info.setValue(20); list.add(info); info = new ExportGxySchoolFlowDto(); info.setSchoolName("齐齐哈尔大学"); info.setDatas("甘肃省"); info.setValue(20); list.add(info); info = new ExportGxySchoolFlowDto(); info.setSchoolName("哈尔滨大学"); info.setDatas("黑龙江"); info.setValue(20); list.add(info); info = new ExportGxySchoolFlowDto(); info.setSchoolName("武汉职业大学"); info.setDatas("北京市"); info.setValue(10); list.add(info); info = new ExportGxySchoolFlowDto(); info.setSchoolName("黑河市大学"); info.setDatas("北京"); info.setValue(10); list.add(info); return list; } public static List<CommonInterfaceDto.ItemBatchDataDto> list6(){ List<CommonInterfaceDto.ItemBatchDataDto> list =new ArrayList<>(); CommonInterfaceDto.ItemBatchDataDto item1 =new CommonInterfaceDto.ItemBatchDataDto(); item1.setSchoolName("双高校"); item1.setData(10); item1.setBatchName("19年"); list.add(item1); CommonInterfaceDto.ItemBatchDataDto item2 =new CommonInterfaceDto.ItemBatchDataDto(); item2.setSchoolName("双高校"); item2.setData(20); item2.setBatchName("20年"); list.add(item2); CommonInterfaceDto.ItemBatchDataDto item3 =new CommonInterfaceDto.ItemBatchDataDto(); item3.setSchoolName("双高校"); item3.setData(30); item3.setBatchName("21年"); list.add(item3); CommonInterfaceDto.ItemBatchDataDto item4 =new CommonInterfaceDto.ItemBatchDataDto(); item4.setSchoolName("双高校"); item4.setData(40); item4.setBatchName("22年"); list.add(item4); return list; } public static List<CommonInterfaceDto.ItemBatchDataDto> list7(){ List<CommonInterfaceDto.ItemBatchDataDto> list =new ArrayList<>(); CommonInterfaceDto.ItemBatchDataDto item1 =new CommonInterfaceDto.ItemBatchDataDto(); item1.setSchoolName("郑州经贸学院"); item1.setData(60); item1.setBatchName("19年"); list.add(item1); CommonInterfaceDto.ItemBatchDataDto item2 =new CommonInterfaceDto.ItemBatchDataDto(); item2.setSchoolName("郑州经贸学院"); item2.setData(10); item2.setBatchName("22年"); list.add(item2); return list; } public static List<Item> list8(){ List<Item> list =new ArrayList<>(); Item item1 =new Item(); item1.setName("计算机"); item1.setValue(10); list.add(item1); Item item2 =new Item(); item2.setName("会计"); item2.setValue(20); list.add(item2); Item item3 =new Item(); item3.setName("销售"); item3.setValue(30); list.add(item3); Item item4 =new Item(); item4.setName("老师"); item4.setValue(40); list.add(item4); Item item5 =new Item(); item5.setName("医学"); item5.setValue(40); list.add(item5); Item item6 =new Item(); item6.setName("农业"); item6.setValue(94); list.add(item6); Item item7 =new Item(); item7.setName("工程"); item7.setValue(100); list.add(item7); return list; } public static Item list9(){ Item item7 =new Item(); item7.setName("工程"); item7.setValue(100); return item7; } public static List<Item> list10(){ List<Item> list =new ArrayList<>(); Item item1 =new Item(); item1.setName("河南省"); item1.setValue("郑州市"); list.add(item1); Item item2 =new Item(); item2.setName("河南省"); item2.setValue("洛阳市"); list.add(item2); Item item3 =new Item(); item3.setName("河南省"); item3.setValue("开封市"); list.add(item3); Item item4 =new Item(); item4.setName("湖北省"); item4.setValue("武汉市"); list.add(item4); Item item5 =new Item(); item5.setName("湖北省"); item5.setValue("襄阳市"); list.add(item5); Item item6 =new Item(); item6.setName("湖北省"); item6.setValue("潜江市"); list.add(item6); Item item7 =new Item(); item7.setName("湖北省"); item7.setValue("荆州市"); list.add(item7); Item item8 =new Item(); item8.setName("北京"); item8.setValue("北京市"); list.add(item8); return list; } }
调用结果: