4 Springboot中使用redis存储集合数据,并模拟条件查询、分页读取
时间:2022-06-17
本文章向大家介绍4 Springboot中使用redis存储集合数据,并模拟条件查询、分页读取,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
前面几篇讲了使用redis存储单个对象,自动缓存、更新、删除的做法,在实际项目中,更常用的是分页查询集合数据,条件查询(譬如按照添加时间倒序排列)。
redis本身是不提供条件查询的,因为是一个非关系型数据库,那么其实通过一些手段,也是能完成条件查询的,尤其是有顺序的条件查询。因为redis里有个zset,这个结构里面存储的数据是有顺序的。
下面就来看看怎么做,接着前几篇的例子讲,以Post表为例。
之前Post的增删改查都是通过我们配置的CachePut,CacheEvict等,自动由框架完成的缓存,这些都是单个Post对象,那我们需要增加一个redis的zset来存储集合,思路就是在新增Post时,通过aop,在zset里也添加一条数据,保存Post的Id和将来要拿来排序用的某个字段做为zset的score。这样就能完成分页排序了。修改和删除时,同理,也在zset里完成相应的修改。
关于aop,我在另外一篇里讲的有。
下面直接来实现。
在controller里加个分页查询的方法:
@RequestMapping("/queryPage")
public Object query(int pageNum, int count) {
return postService.queryPage(pageNum, count);
}
在repository里加上分页查询的接口
@CacheConfig(cacheNames = "post")
public interface PostRepository extends PagingAndSortingRepository<Post, Integer> {
@Cacheable(key = "'PostId' + #p0")
Post findById(int id);
/**
* 新增或修改时
*/
@CachePut(key = "'PostId' + #p0.id")
@Override
Post save(Post post);
@Transactional
@Modifying
@CacheEvict(key = "'PostId' + #p0")
int deleteById(int id);
/**
* 正序分页
*/
List<Post> findAllOrderByWeight(Pageable pageable);
/**
* 倒序分页,注意:findAll后面要加个By,否则会报错启动不了
*/
List<Post> findAllByOrderByWeightDesc(Pageable pageable);
}
pom里加上aop
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
在service层加上aop切面:
@Service
public class PostService {
@Autowired
private PostRepository postRepository;
@Autowired
private RedisTemplate redisTemplate;
public Post findById(int id) {
return postRepository.findById(id);
}
public Post save(Post post) {
return postRepository.save(post);
}
public int delete(int id) {
return postRepository.deleteById(id);
}
public List<Post> queryPage(int pageNum, int count) {
//根据weight倒序分页查询
// Pageable pageable = new PageRequest(pageNum, count, Sort.Direction.DESC, "weight");
Pageable pageable = new PageRequest(pageNum, count);
return postRepository.findAllByOrderByWeightDesc(pageable);
}
}
定义一个aspect切面
package com.tianyalei.aspect;
import com.tianyalei.domain.Post;
import com.tianyalei.repository.PostRepository;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* Created by wuwf on 17/4/28.
* aop切面处理
*/
@Component
@Aspect
public class PostAspect {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private PostRepository postRepository;
private final String POST_SET_KEY = "post_set";
@Pointcut(value = "within(com.tianyalei.service.PostService)")
public void postAccess() {
}
@Around("postAccess()")
public Object around(ProceedingJoinPoint pjp) {
//方法名
String methodName = pjp.getSignature().getName();
//参数
Object[] objects = pjp.getArgs();
//分页查询
if ("queryPage".equals(methodName)) {
int pageNum = (int) objects[0];
int count = (int) objects[1];
try {
//倒序查询分页的ids
Set<Integer> ids = redisTemplate.opsForZSet().reverseRange(POST_SET_KEY, pageNum * count, pageNum * count + count - 1);
List<Post> posts = new ArrayList<>(ids.size());
for (int id : ids) {
posts.add(postRepository.findById(id));
}
return posts;
} catch (Exception e) {
try {
return pjp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
} else if("save".equals(methodName)) {
Post post = null;
try {
post = (Post) pjp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
redisTemplate.opsForZSet().add(POST_SET_KEY, post.getId(), post.getWeight());
return post;
} else if("delete".equals(methodName)) {
int id = (int) objects[0];
redisTemplate.opsForZSet().remove(POST_SET_KEY, id);
}
try {
return pjp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
}
这里通过方法名来区分是add、delete还是查询,然后在redis里做相应的处理。
可以通过添加多条数据来测试一下,缓存是否生效。
上面这个只是实现了逻辑,还有一些异常处理数据同步的没有处理,只讲了思路。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- select 进阶查询
- Codeforces Round #627 (Div. 3) E. Sleeping Schedule (DP)
- PAT (Advanced Level) Practice 1144 The Missing Number (20分)
- JDBC 基础操作
- Codeforces Round #625 (Div. 2, based on Technocup 2020 Final Round) C. Remove Adjacent
- MySQL 存储过程
- MySQL 约束
- MySQL 中的流程控制语句
- MySQL 权限操作
- MySQL 事务
- Java 随机生成四则运算式并生成答案
- MySQL 常用函数汇总
- Leetcode 698. 划分为k个相等的子集
- java数据结构与算法-快速排序
- 线上环境 Linux 系统调用追踪