redis 缓存设置,实现类似putIfAbsent功能

缓存查询为空

在使用缓存过程中,我们都会面临没有查询到数据的情况,然后再把结果查询结果设置到缓存中,这样的场景有很多

常规实现

我们先用普通的方法来实现一下

  • 缓存列表
private List<InfoCategory> queryAllCategory(String tid) {
        String key = String.format(Constants.ALL_CATEGORY,tid);
        String value = redisTemplate.opsForValue().get(key);
        if(!StringUtils.isEmpty(value)){
            return JSON.parseArray(value,InfoCategory.class);
        }else{
            List<InfoCategory> list = new LambdaQueryChainWrapper<>(categoryMapper)
                    .eq(InfoCategory::getTid, tid)
                    .list();
            if(!CollectionUtils.isEmpty(list)){
                redisTemplate.opsForValue().set(key, JSON.toJSONString(list));
            }
            return list;
        }
}
  • 缓存普通对象
public CategoryEventListResponse queryEventName(String tid, String appletId, String eventName, String taskSerial) {
        String key  = String.format(Constants.EVENT_TASK_NAME,tid,appletId,eventName,taskSerial);
        String value = redisTemplate.opsForValue().get(key);
        if(!StringUtils.isEmpty(value)){
            return JSON.parseObject(value,CategoryEventListResponse.class);
        }else{
            CategoryEventListResponse response =doQueryEventName(tid, appletId, eventName, taskSerial);
            if(Objects.isNull(response)){
                response = new CategoryEventListResponse();
            }
            redisTemplate.opsForValue().set(key, JSON.toJSONString(response),2*60, TimeUnit.SECONDS);
            return response;
        }
}

缺点:很多相似的功能基本上都如同上面的代码实现,比较繁琐、累赘,代码复用性不强

优化方案

1、在获取缓存的时候,直接把一个回调方法传过去,在没有数据的情况下可以直接运行回调方法缓存值
2、需要封装的查询,回调方法全部统一封装,调用者甚至感觉不到存在回调方法

直接上最终版代码

封装redisTemplate
@Component
@Data
@Slf4j
public class CallBackRedisTemplate {
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    /**
     *
     * @param key  redis key
     * @param callable callable function
     * @param vClass return type class
     * @param defaultValue when callable function get null,set defaultValue to avoid to call function again,storage like {} []
     * @param timeUnit
     * @param time
     * @param <V>
     * @return
     */
    public <V> V get(String key, Callable<V> callable,Class<V> vClass,V defaultValue, TimeUnit timeUnit,Long time) {
        String redisValue =  redisTemplate.opsForValue().get(key);
        if(StringUtils.isEmpty(redisValue)){
            try {
                V v = callable.call();
                if(Objects.isNull(v)){
                    redisTemplate.opsForValue().set(key,JSON.toJSONString(defaultValue),time,timeUnit);
                    return defaultValue;
                }else{
                    redisTemplate.opsForValue().set(key,JSON.toJSONString(v),time,timeUnit);
                    return v;
                }

            } catch (Exception e) {
                e.printStackTrace();
                log.error("callable error key={}",key,e);
                return null;
            }
        }

        return JSON.parseObject(redisValue,vClass);
    }


    /**
     *
     * @param key  redis key
     * @param callable callable function
     * @param vClass return type class
     * @param timeUnit
     * @param time
     * @param <V>
     * @return
     */
    public <V> V get(String key, Callable<V> callable,Class<V> vClass, TimeUnit timeUnit,Long time) {
        String redisValue =  redisTemplate.opsForValue().get(key);
        if(StringUtils.isEmpty(redisValue)){

            try {
                V v = callable.call();
                if (!Objects.isNull(v)) {
                    redisTemplate.opsForValue().set(key, JSON.toJSONString(v), time, timeUnit);
                }
                return v;
            } catch (Exception e) {
                e.printStackTrace();
                log.error("callable error key={}",key,e);
                return null;
            }
        }

        return JSON.parseObject(redisValue,vClass);
    }


    /**
     *
     * @param key  redis key
     * @param callable callable function
     * @param typeReference return reference type class
     * @param defaultValue when callable function get null,set defaultValue to avoid to call function again,storage like {} []
     * @param timeUnit
     * @param time
     * @param <V>
     * @return
     */
    public <V> V get(String key, Callable<V> callable,TypeReference<V> typeReference,V defaultValue, TimeUnit timeUnit,Long time) {
        String redisValue =  redisTemplate.opsForValue().get(key);
        if(StringUtils.isEmpty(redisValue)){

            try {
                V v = callable.call();
                if(Objects.isNull(v)){
                    redisTemplate.opsForValue().set(key,JSON.toJSONString(defaultValue),time,timeUnit);
                    return defaultValue;
                }else{
                    redisTemplate.opsForValue().set(key,JSON.toJSONString(v),time,timeUnit);
                    return v;
                }

            } catch (Exception e) {
                e.printStackTrace();
                log.error("callable error key={}",key,e);
                return null;
            }
        }

        return JSON.parseObject(redisValue,typeReference);
    }

    /**
     *
     * @param key  redis key
     * @param callable callable function
     * @param typeReference return reference type class
     * @param timeUnit
     * @param time
     * @param <V>
     * @return
     */
    public <V> V get(String key, Callable<V> callable,TypeReference<V> typeReference, TimeUnit timeUnit,Long time) {
        String redisValue =  redisTemplate.opsForValue().get(key);
        if(StringUtils.isEmpty(redisValue)){

            try {
                V v = callable.call();
                if (!Objects.isNull(v)) {
                    redisTemplate.opsForValue().set(key, JSON.toJSONString(v), time, timeUnit);
                }
                return v;

            } catch (Exception e) {
                e.printStackTrace();
                log.error("callable error key={}",key,e);
                return null;
            }
        }
        return JSON.parseObject(redisValue,typeReference);
    }
}

  • 反序列化处理

这里有一个点,就是回调方法返回的是一个泛型V,在fastJson反序列化的时候,需要确定V的calss类型,所以添加了参数 Class<V> vClass 。当返回值为列表形式的时候,我们在使用反序列化就需要使用到 TypeReference<V> typeReference

  • 在回调过程中没有值,下次不希望再来调用回调方法,减少对数据库的压力

添加参数 V defaultValue ,给缓存系统直接设置到缓存服务中,避免对数据库或者服务造成压力

封装业务service
  • EventBizCacheService
public interface EventBizCacheService {
    /**
     * cache all category
     * @param tid
     * @return
     */
    List<InfoCategory> queryAllCategory(String tid);
    /**
     * cache all event name
     * @param tid
     * @return
     */
    CategoryEventListResponse queryEventName(String tid, String appletId, String eventName, String taskSerial);
}
  • impl
@Service
public class EventBizCacheServiceImpl implements EventBizCacheService {

    @Autowired
    private CallBackRedisTemplate callBackRedisTemplate;

    @Autowired
    private InfoCategoryMapper categoryMapper;

    @Autowired
    private InfoCategoryEventRelMapper relMapper;

    @Autowired
    private InfoTenantAppletEventMapper eventMapper;

    @Autowired
    private InfoEventTaskMapper taskMapper;

    @Autowired
    private CrowCategoryService crowCategoryService;

    @Override
    public List<InfoCategory> queryAllCategory(String tid) {
        String key = String.format(Constants.ALL_CATEGORY,tid);
        return  callBackRedisTemplate.get(key,
                () -> new LambdaQueryChainWrapper<>(categoryMapper).eq(InfoCategory::getTid, tid).list(),
                new TypeReference<List<InfoCategory>>(){},
                TimeUnit.DAYS,
                360L);
    }
    @Override
    public CategoryEventListResponse queryEventName(String tid, String appletId, String eventName, String taskSerial) {
        String key  = String.format(Constants.EVENT_TASK_NAME,tid,appletId,eventName,taskSerial);
        return  callBackRedisTemplate.get(key,
                ()->{
                    CategoryEventListResponse cResult = crowCategoryService.queryEventName(appletId, eventName, taskSerial);
                    if (cResult != null) {
                        return cResult;
                    }
                    return categoryMapper.queryEventName(appletId, eventName, taskSerial);

                },
                CategoryEventListResponse.class,
                TimeUnit.DAYS,
                360L);
    }
}

这个业务类就是为了屏蔽callable方法,时调用者无需关系数据来源

总结

在业务开发过程中遇到代码实现差不多,只有部分业务功能不一样的时候,考虑通用方案来解决,把业务方法传递过去,让调用者通过lambda方法自己完成业务方法。
如果有更好的方法,还请大家不吝赐教。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis缓存可以通过设置键的过期时间来实现订单自动取消功能。当订单生成时,我们将订单信息存储到Redis缓存中,并设置一个过期时间(比如30分钟)。这样,订单信息将在30分钟后自动从Redis中移除。如果订单在30分钟内完成支付,我们可以根据订单号在Redis中查找订单信息并进行相应处理。如果订单在30分钟内未完成支付,则订单信息会在过期时间到达时自动从Redis中移除,并触发订单取消逻辑。 在实现过程中,可以使用Redis的SET命令将订单信息保存到Redis中,并通过EXPIRE命令设置该键的过期时间。例如: ``` SET order:12345 "待支付订单信息" EXPIRE order:12345 1800 ``` 以上代码将"待支付订单信息"保存在键名为"order:12345"的键中,并设置其过期时间为1800秒(30分钟)。当订单完成支付时,可以通过GET命令获取订单信息。如果返回空值,则说明订单已被取消。如果返回订单信息,则可以继续处理。 另外,为了保证系统的可靠性,可以在订单取消时触发一些额外的操作,如发送通知给用户或其他相关系统。可以使用Redis的发布/订阅功能,将订单取消事件发布到指定的频道,然后其他订阅该频道的系统可以接收到该事件并执行相应的处理逻辑。 总之,通过使用Redis缓存功能,可以轻松实现订单自动取消的功能,并且具备一定的灵活性和可扩展性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值