简单调用Redis缓存的工具方法
前言:
使用redis缓存数据时,频繁的get,set方法,十分的繁琐与难以维护,今天刚好遇上这个场景写了一个通用的工具方法,将代码贴出,有需要可以取走。附上简单的说明,如有不清楚可以评论咨询,如有不足或者更好的想法也可以一起讨论。
简单暴力,毕竟是工具类直接贴代码。
备注:因为不是去说redis接入的,redis接入部分的代码我就不贴了。
1、工具类的代码:
今天刚写的,提供个简单思路,有需要的可以自己拓展一下。
思路如下:
1.1、主要是使用的 Callable类,将所有真正查询的逻辑视作一个简单的函数,一个单位,抽象出来交由 Callable去调用。抽象与实现分离的思维。
1.2、Redis存的都是字符串,但有时是数组有时是对象,使用JSON工具时仍需要注意此项,因此封装了两个方法,区别开。
1.3、将类交由Spring管理,首先是单例,其次也可以方便使用其他的一些操作。
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
public class RedisUtil {
@Autowired
private RedisTemplate redisTemplate;
/**
* redis缓存的封装 返回是数组类型
*
* @param callable 具体逻辑
* @param redisKey
* @param clazz 返回值的类型
* @param expireTime 过期时间(单位毫秒)
* @return java.util.List<R>
* @date 16:07 2023/3/14
**/
public <R> List<R> cacheCallableArray(Callable<List<R>> callable, String redisKey, Class<R> clazz, Long expireTime) throws Exception {
log.info("method:cacheCallableArray,redisKey:{},expireTime:{}", redisKey, expireTime);
// 取缓存
Object redisValue = redisTemplate.opsForValue().get(redisKey);
if (ObjectUtil.isNotEmpty(redisValue)) {
return JSONArray.parseArray(String.valueOf(redisValue), clazz);
}
// 存到缓存
String jsonString = JSONArray.toJSONString(callable.call());
redisTemplate.opsForValue().set(redisKey, jsonString, expireTime, TimeUnit.MILLISECONDS);
return callable.call();
}
/**
* redis缓存的封装 返回是对象类型
*
* @param callable 具体逻辑
* @param redisKey
* @param clazz 返回值的类型
* @param expireTime 过期时间(单位毫秒)
* @return R
* @date 16:11 2023/3/14
**/
public <R> R cacheCallableObj(Callable<R> callable, String redisKey, Class<R> clazz, Long expireTime) throws Exception {
log.info("method:cacheCallableArray,redisKey:{},expireTime:{}", redisKey, expireTime);
// 取缓存
Object redisValue = redisTemplate.opsForValue().get(redisKey);
if (ObjectUtil.isNotEmpty(redisValue)) {
return JSONObject.parseObject(String.valueOf(redisValue), clazz);
}
// 存到缓存
String jsonString = JSONObject.toJSONString(callable.call());
redisTemplate.opsForValue().set(redisKey, jsonString, expireTime, TimeUnit.MILLISECONDS);
return callable.call();
}
}
2、贴一下调用方的代码,主要是提供键与过期时间。部分无关的逻辑简写了。
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class A{
@Autowired
private RedisUtil redisUtil;
/**
* XXX 缓存前缀
*/
private final String XXX_CACHE = "XXX_CACHE :";
/**
* XXX查询 缓存
* @date 16:18 2023/3/14
* @param id
**/
@SneakyThrows
public CascaderEntity getTreeModelsCascaderCache(Long id) {
String cacheKey = this.CATALOG__PC_getTreeModelsCascader_CACHE + String.valueOf(id);
Long expireTime = streamingCommonUtil.randomLong();
return redisUtil.cacheCallableObj(()->this.getTreeModelsCascader(id),cacheKey,CascaderEntity.class,expireTime);
}
/**
* 原查询方法
* 树结构的一种递归逻辑,有兴趣的同志可以瞅瞅
* @param id
* @return com.central.common.vo.base.CascaderEntity 仿照前端elementUi的Cascader级联下拉框的数据模型
* @date 10:52 2023/3/14
**/
public CascaderEntity getTreeModelsCascader(Long id) {
// 查询所有树,并数据处理
List<CatalogContent> catalogContents = this.list();
Assert.isArrayEmpty(catalogContents, CommonEnum.ExceptionDesc.数据异常);
// 根据父id分组
Map<Long, List<CatalogContent>> map = catalogContents.stream().collect(Collectors.groupingBy(CatalogContent::getParentId));
CatalogContent topCatalogContent = map.get(-1L).get(0);
return this.cascaderHandle(topCatalogContent, map);
}
/**
* 递归树结构处理
* 业务限制,顶级树节点只有一层。有需要可以换成List<Node>实现,逻辑差不多
* @param catalogContent 当前节点对象
* @param map 整个树的数据,根据parentId分组
* @return com.central.common.vo.base.CascaderEntity
* @date 10:52 2023/3/14
**/
private CascaderEntity cascaderHandle(CatalogContent catalogContent, Map<Long, List<CatalogContent>> map) {
CascaderEntity cascaderEntity = new CascaderEntity();
cascaderEntity.setValue(String.valueOf(catalogContent.getId())).setLabel(catalogContent.getName());
// 获取子节点
List<CatalogContent> childNodes = map.get(catalogContent.getId());
if (CollectionUtil.isEmpty(childNodes)) {
return cascaderEntity;
}
// 按指定sort排序
Collections.sort(childNodes, (x1, x2) -> streamingCommonUtil.sortAsc(x1.getSort(), x2.getSort()));
List<CascaderEntity> newChildNodes = new ArrayList<>();
// 递归
childNodes.stream().forEach(item -> {
CascaderEntity newChildNode = this.cascaderHandle(item, map);
newChildNodes.add(newChildNode);
});
cascaderEntity.setChildren(newChildNodes);
return cascaderEntity;
}
}
package com.central.common.vo.base;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.List;
/**
* Cascader 级联选择器的模型
* @author chenkaimin
* @Date 2022/11/2 14:38
*/
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class CascaderEntity implements Serializable {
private static final long serialVersionUID = -2016602847223469602L;
/**
* 值,相当于id
*/
private String value;
/**
* 展示,相当于name
*/
private String label;
/**
* 子级
*/
private List<CascaderEntity> children;
public CascaderEntity(String value, String label) {
this.value = value;
this.label = label;
}
public CascaderEntity(Long value, String label) {
this.value = String.valueOf(value);
this.label = label;
}
}