springboot整合redis实现分页
最近写springboot项目时遇到一个问题,就是使用redis实现分页。
在网上差了很多,看到了一个使用ZSet实现的,但是最后也没看懂,最后想了下想还是用list+map实现。
就拿做的订单分页来说,源码奉上
public Result queryAllOrderHave(Integer userId,Integer pageNo,Integer pageSize) {
//用来判断这个用户是否存在,不展示这个方法的源码了
if(orderMapper.userExistOrNo(userId)<=0) {
return Result.fail("错误:用户不存在");
}
//获取该用户已支付订单的总记录数
int allCount = orderMapper.haveOrderCount(userId);
//这是封装的一个分页的方法,源码在下面展示
Integer[] pageAndPageSize = PageUtil.getPageAndPageSize(allCount, pageNo, pageSize);
if(pageAndPageSize==null){
return Result.fail("请检查pageNo和pageSize");
}
//根据分页 显示出分页查询时需要的数据 redis中list的range 如果结束的位置超过总数量,默认为最后一个
//显示该用户所有未支付订单
//关键就在于后面的pageAndPageSize[0]和记录数pageAndPageSize[0]+pageAndPageSize[1]-1
List<Object> range1 = redisUtil.range(RedisNameUtil.setOrderHave(userId),pageAndPageSize[0], pageAndPageSize[0]+pageAndPageSize[1]-1);
//如果该key已经存在,直接从redis数据库里取,不用再遍历一遍
if(redisUtil.hasKey(RedisNameUtil.setOrderHave(userId))){
return Result.success(range1,allCount);
}else{
//从数据库查出数据
Map<Object, Object> stringObjectMap = orderMapper.queryAllOrderHaveByUserId(userId);
for (Map.Entry<Object,Object> entry : stringObjectMap.entrySet()){
//将从数据里查出来的数据装到redis中的list集合内
redisUtil.rightPush(RedisNameUtil.setOrderHave(userId), entry.getValue());
}
//和上面的一样了
List<Object> range = redisUtil.range(RedisNameUtil.setOrderHave(userId), pageAndPageSize[0], pageAndPageSize[0]+pageAndPageSize[1]-1);
return Result.success(range,allCount);
}
}
对源码的解释:
List<Object> range1 = redisUtil.range(RedisNameUtil.setOrderHave(userId),pageAndPageSize[0], pageAndPageSize[0]+pageAndPageSize[1]-1);
为封装的一个工具,用来自定义遍历redis里list的集合指定下标的数据 范围:[start,end]
/**
* 自定义遍历的序列
* @param key key
* @param start 开始下标 (第一个0)
* @param end 结束下标 (最后一个-1)
* @return list
*/
public List<Object> range(String key,long start,long end){
try {
if(!redisTemplate.hasKey(key)){
return null;
}
return redisTemplate.opsForList().range(key,start,end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
接着是封装的分页工具类
/**
* @Create on 2021/12/3 16:51
*/
public class PageUtil {
public static Integer[] getPageAndPageSize(Integer allCount,Integer pageNo,Integer pageSize) {
if (pageNo >= 1 && pageSize >= 1) {
//第pageNo页 从第(pageNo-1)*pageSize个下标开始往下数
int maxPage = (allCount % pageSize )== 0 ? (allCount / pageSize) : (allCount / pageSize) + 1;
if (pageNo >= maxPage) {
pageNo = (maxPage - 1) * pageSize;
} else {
pageNo = (pageNo - 1) * pageSize;
}
return new Integer[]{pageNo,pageSize};
}else{
return null;
}
}
}
可以看到,如果有问题的话返回的是null,否则返回一个Integer类型的数组
然后这两行是获取传进来的pageNo和pageSize
Integer[] pageAndPageSize = PageUtil.getPageAndPageSize(allCount, pageNo, pageSize);
List<Object> range1 = redisUtil.range(RedisNameUtil.setOrderHave(userId),pageAndPageSize[0], pageAndPageSize[0]+pageAndPageSize[1]-1);
关键来了,为什么后面的()的start=pageAndPageSize[0],这个start是经过处理过的pageNo,和普通的分页没什么好说的,因为mysql里的limit是从第几行开始多少条数据,但是().range(key,start,end)里的start和end表示从第几个下标开始,显示到第几个下标的数据。
简单来说,对于mysql:
SELECT * FROM table LIMIT 5,10; //检索第6条到第15条数据 共十条
对于redis
redisTemplate.opsForList().range(key,5,10); //检索第6条到第11条数据 共6条
pageAndPageSize[0]表示处理过的页码数,不难看出,处理过的页码数加上页面总数-1就和上面的limit一样了
即 pageAndPageSize[0]+pageAndPageSize[1]-1