今天在进行项目的时候需要Redis实现一个功能
需求:Redis hash类型模糊查询,同时对结果进行分页
首先是Redis hash类型模糊查询
//Cusor中存储的是查询key对应的Map
Cursor<Map.Entry<String,String>> cursor = redisTemplate
.opsForHash()
.scan("zhDicGoods", ScanOptions.scanOptions() //绑定模糊查询的hash的key
.match(name+"*") //模糊查询规则
.count(10000).build());
其中关于match参数pattern
*:通配任意多个字符
?:通配单个字符
[]:通配括号内的某一个字符
再此遇到了一个问题,就是在模糊查询的时候**“查询内容+*”**可能查询不到,原因是redis默认的导入数据的时候可能存在乱码的问题
就是遇到\xca\xed加上你key之类的乱码!
你需要重新定义你导入key的内容
在启动类中加入一个Bean
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
redisTemplate.setConnectionFactory(factory);
//key序列化方式;(不然会出现乱码;),但是如果方法上有Long等非String类型的话,会报类型转换错误;
//所以在没有自己定义key生成策略的时候,以下这个代码建议不要这么写,可以不配置或者自己实现ObjectRedisSerializer
//或者JdkSerializationRedisSerializer序列化方式;
RedisSerializer<String> redisSerializer = new StringRedisSerializer();//Long类型不可以会出现异常信息;
redisTemplate.setKeySerializer(redisSerializer);
redisTemplate.setHashKeySerializer(redisSerializer);
return redisTemplate;
以上解决了redis模糊查询的问题,接下来就是模糊查询的结果返回分页
此处引用网上的资料,我会在末尾标注出来
利用ZSET及HASH结构存储数据实现redis分页。
1. 首先利用ZSET将表A中的id以value形式进行存储,以及利用ZSET中score进行排序处理; 2. 将表A中数据以HASH结构进行存储,id作为HASH中key; 3. 利用redis中的zRangeByScore进行ZSET分页取出id列表,然后即可取出HASH中分页后的数据。
先创建一个pageUtil
@Component
public class PageUtil {
@Autowired
private RedisTemplate redisTemplate;
/**
* 存放单个hash缓存
* @param key 键
* @param hkey 键
* @param value 值
* @return
*/
public boolean hput(String key, String hkey, Object value) {
try {
redisTemplate.opsForHash().put(key, hkey, value);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 分页存取数据
* @param key hash存取的key
* @param hkey hash存取的hkey
* @param score 指定字段排序
* @param value
* @return
*/
public boolean setPage(String key, String hkey, double score, String value){
boolean result = false;
try {
result=redisTemplate.opsForZSet().add(key + ":page", hkey, score);
//result = hput(key, hkey, value);
} catch (Exception e) {
e.printStackTrace();
}
//设置辅助分页的过期时间
redisTemplate.expire(key+":page",1800000 , TimeUnit.MILLISECONDS);
//redisTemplate.expire(key,60000 , TimeUnit.MILLISECONDS);
return result;
}
/**
* 分页取出 hash中hkey值
* @param key
* @param offset
* @param count
* @return
*/
public Set<String> getPage(String key, int offset, int count){
Set<String> result = null;
try {
result = redisTemplate.opsForZSet().rangeByScore(key+":page", 1, 100000, (offset-1)*count, count);//1 100000代表score的排序氛围值,即从1-100000的范围
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 计算key值对应的数量
* @param key
* @return
*/
public Integer getSize(String key){
Integer num = 0;
try {
Long size = redisTemplate.opsForZSet().zCard(key+":page");
return size.intValue();
} catch (Exception e) {
e.printStackTrace();
}
return num;
}
}
此处offset就是页码, count就是每页显示的条数
调用就是在存储hash的时候存储一个对应的分页数据
/**
* 生成辅助分页
* @param cursor
* @param name:会在pageutil中自动加上:page
*/
private void setPage(Cursor<Map.Entry<String,String>> cursor,String name,String tableName){
//setPage初始化值,zset的sort值
int i = 1;
//遍历模糊查询结果,将模糊查询的结果生成分页数据,用zset存储模糊查询的数据排序
//存储名称是查询name+:page,值是hash类型的key
while (cursor.hasNext()){
Map.Entry<String,String> result= cursor.next();
//获取key
String key = result.getKey();
//获取value
String value = result.getValue();
//存储对应的辅助分页数据
pageUtil.setPage(tableName+name,key,i,null);
i++;
}
}
然后就是查询数据的时候不从hash中查询,从zset中查询到hkey,然后在查询hash类型
/**
* 获取分页的总数据,并且加入到map中
* @param name
* @param currentPage
* @param count
* @param resultMap
* @param result
*/
private void getPageResult(String name,int currentPage, int count,Map resultMap,ArrayList result,String tableName,T t){
//得到分页数据总条数
Integer totalNumber = pageUtil.getSize(tableName+name);
//得到对应分页的key数组
Set<String> keyPages = pageUtil.getPage(tableName+name, currentPage, count);
//遍历循环查询对应cout对应的数据,同时转为对象输入到List
count =keyPages.size();
for (String keyPage : keyPages) {
//根据zset的key查询hash对应的数据
String JSONObject = (String) redisTemplate.boundHashOps(tableName).get(keyPage);
//转为对应的实体类
T Object = (T) JSON.parseObject(JSONObject, (Type) t);
result.add(Object);
}
//返回总页数
Integer totalPage;
if(totalNumber<=count){
totalPage=1;
}else {
totalPage =totalNumber/10+1;
}
//加入返回实体类结果,总条数,起始页数,总条数
resultMap.put("result",result);
resultMap.put("totalNumber",totalNumber);
resultMap.put("currentPage",currentPage);
resultMap.put("count",count);
resultMap.put("totalPage",totalPage);
}
模糊查询以及分页我设置了一个工具类,总的是
@Component
public class RedisFurryAndPageQueryUtil<T> {
//插入辅助分页
@Autowired
private PageUtil pageUtil;
//注入redis模板工具
@Autowired
private RedisTemplate redisTemplate;
/**
* 根据条件进行模糊查询查询并返回分页结果
* @param name
* @param currentPage
* @param count
* @return
*/
public Map<String,Object> find(String name, int currentPage, int count,String tableName,T t) {
//将传入的参数变为小写
name = StringUtils.lowerCase(name);
//map用来存储查询到的结果以及分页的总数据
Map<String,Object> map = new HashMap<>();
//用来存储查询到的ZhDicGoods
ArrayList<ZhDicGoods> result = new ArrayList<>();
//定义pageName用来查看redis中是否存在对应的辅助分页key-value
String pageName = tableName+name + ":page";
//是否存在对应的辅助分页辅助分页key-value
Boolean ifExist = redisTemplate.hasKey(pageName);
//如果不存在,生成辅助分页,同时查询
if(!ifExist){
try {
//如果传入参数为空则查询所有
if(name==null||"".equals(name)){
//模糊查询返回结果
//Cusor中存储的是查询key对应的Map
Cursor<Map.Entry<String,String>> cursor = redisTemplate
.opsForHash()
.scan(tableName, ScanOptions.scanOptions() //绑定模糊查询的hash的key
.match("*") //模糊查询规则
.count(1000).build());
}
//模糊查询返回结果
//Cusor中存储的是查询key对应的Map
Cursor<Map.Entry<String,String>> cursor = redisTemplate
.opsForHash()
.scan(tableName, ScanOptions.scanOptions() //绑定模糊查询的hash的key
.match(name+"*") //模糊查询规则
.count(10000).build());
//生成分页key-value
setPage(cursor,name,tableName);
//生成查询结果并且放入map中
getPageResult(name,currentPage,count,map,result,tableName,t);
cursor.close();
} catch (Exception e) {
e.printStackTrace();
}
}else{
//有辅助分页key-value,直接查询结果放入map中
getPageResult(name,currentPage,count,map,result,tableName,t);
}
//返回result
return map;
}
/**
* 生成辅助分页
* @param cursor
* @param name:会在pageutil中自动加上:page
*/
private void setPage(Cursor<Map.Entry<String,String>> cursor,String name,String tableName){
//setPage初始化值,zset的sort值
int i = 1;
//遍历模糊查询结果,将模糊查询的结果生成分页数据,用zset存储模糊查询的数据排序
//存储名称是查询name+:page,值是hash类型的key
while (cursor.hasNext()){
Map.Entry<String,String> result= cursor.next();
//获取key
String key = result.getKey();
//获取value
String value = result.getValue();
//存储对应的辅助分页数据
pageUtil.setPage(tableName+name,key,i,null);
i++;
}
}
/**
* 获取分页的总数据,并且加入到map中
* @param name
* @param currentPage
* @param count
* @param resultMap
* @param result
*/
private void getPageResult(String name,int currentPage, int count,Map resultMap,ArrayList result,String tableName,T t){
//得到分页数据总条数
Integer totalNumber = pageUtil.getSize(tableName+name);
//得到对应分页的key数组
Set<String> keyPages = pageUtil.getPage(tableName+name, currentPage, count);
//遍历循环查询对应cout对应的数据,同时转为对象输入到List
count =keyPages.size();
for (String keyPage : keyPages) {
//根据zset的key查询hash对应的数据
String JSONObject = (String) redisTemplate.boundHashOps(tableName).get(keyPage);
//转为对应的实体类
T Object = (T) JSON.parseObject(JSONObject, (Type) t);
result.add(Object);
}
//返回总页数
Integer totalPage;
if(totalNumber<=count){
totalPage=1;
}else {
totalPage =totalNumber/10+1;
}
//加入返回实体类结果,总条数,起始页数,总条数
resultMap.put("result",result);
resultMap.put("totalNumber",totalNumber);
resultMap.put("currentPage",currentPage);
resultMap.put("count",count);
resultMap.put("totalPage",totalPage);
}
}
以上就是此次工作的整体步骤
查看的文档还有博客有
spring中redisTemplate实现redis分页
https://segmentfault.com/a/1190000020866225
redis 模糊查询
https://blog.csdn.net/zhaipengfei1231/article/details/80819454?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.edu_weight&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.edu_weight
scan实现模糊查询redis数据
https://blog.csdn.net/yuan_sun/article/details/102663986?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-9.edu_weight&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-9.edu_weight
Springdataredis Api文档
https://docs.spring.io/spring-data/redis/docs/2.2.4.RELEASE/api/
一些小知识
StringRedisTemplate/RedisTemplate设置过期时间
https://blog.csdn.net/qq_35192741/article/details/91448395
//redistemplate是否包含key
public Boolean hasKey(K key)
//子类继承父类的文档注释
ctrl+O 弹出的窗口下面把 勾下 JavaDoc 就可以了