Spring Boot整合JPA+MySQL+Redis(二)

前言

在之前的发布的博客https://blog.csdn.net/IceCaptain/article/details/79200388已经做过一次相关整合,这篇文章主要是记录一些注意点和非注解的缓存方式

*注:这篇文章的所有配置都是基于上一篇文章,只是变更了实体类,删除了redisKey属性,并且将使用的service类直接继承了RedisService,其他的大致相同,在这里只演示RedisService的继承类RemindPushManager,重点展示思路。

开始

redis作为缓存常用的无非两种形式,一种是注解形式,另一种则是非注解形式,虽然非注解形式相比起来更为麻烦,但是灵活性更高,可塑性更强

一、注解缓存
在上一篇文章中记录的即是以注解的方式使用缓存,但存在两个问题:
*问题一:前文UserServiceImpl类的save模块将数据手动插入了redis客户端,这是多余的,笔者之前对redis理解尚浅,不知道类似于下图的方式是spring存入的redis中,手动存入将使redis客户端中存在了key不同value相同的两份数据。
这里写图片描述
*问题二:前文虽然实现了注解缓存,但是实现的非常勉强,注解@CacheEvict(value=”userCache”, allEntries=true)中的allEntries=true在每一次执行完方法后都会清除掉所有缓存,虽然保证了findByIdfindAll(list)两个方法不会得到过期缓存,但是代价太过沉重,只适用于几乎没有什么修改变更的系统中,太过局限。

一种思路
下面提供一种方式,能协调findById和修改操作,但是放弃了findAll(list)的方式,暂时没有找到这个方法和其他方法在注解缓存中的平衡点。
在注解中增加了key属性,指定了spring存入redis中key的类型及数据,这样做在执行修改操作时,只会清除掉对应id的缓存

package com.java.manager;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.java.DO.RemindPushDO;
import com.java.common.CommonRestResult;
import com.java.dao.RemindPushDAO;
import com.java.enums.StatusEnum;
import com.java.form.RemindPushForm;
import com.java.servive.redis.RedisService;
import com.java.vo.RemindPushVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class RemindPushManager extends RedisService {

    private static final String REMIND_KEY = "REMIND_KEY";

    @Autowired
    private RemindPushDAO remindPushDAO;

    public List list(String status){
        return remindPushDAO.getByStatus(status).stream().map(remindPushDO -> {
            return getById(remindPushDO.getId());
        }).collect(Collectors.toList());
    }

    @Cacheable(value="remindPushCache", key="#id")
    public RemindPushVO getById(Long id){
        RemindPushDO remindPushDO = remindPushDAO.findOne(id);
        String str = JSON.toJSONString(remindPushDO);
        RemindPushVO remindPushVO = JSON.parseObject(str, RemindPushVO.class);
        return remindPushVO;
    }

    @CachePut(value="remindPushCache", key="#remindPushForm.id")
    @Transactional
    public RemindPushVO create(RemindPushForm remindPushForm){
        String str = JSON.toJSONString(remindPushForm);
        RemindPushDO remindPushDO = JSON.parseObject(str, RemindPushDO.class);
        remindPushDO.setStatus(StatusEnum.NORMAL);
        remindPushDO.setCreateTime(new Date());
        remindPushDO.setUpdateTime(new Date());
        Long id = remindPushDAO.save(remindPushDO).getId();
        return getById(id);
    }


    @CacheEvict(value="remindPushCache", key="#remindPushForm.id")
    @Transactional
    public RemindPushVO modify(RemindPushForm remindPushForm){
        RemindPushDO oldRemindPushDO = remindPushDAO.findOne(remindPushForm.getId());
        if(oldRemindPushDO == null){
            return new RemindPushVO();
        }

        String str = JSON.toJSONString(remindPushForm);
        RemindPushDO newRemindPushDO = JSON.parseObject(str, RemindPushDO.class);

        newRemindPushDO.setStatus(oldRemindPushDO.getStatus());
        newRemindPushDO.setCreateTime(oldRemindPushDO.getCreateTime());
        newRemindPushDO.setUpdateTime(new Date());
        Long id = remindPushDAO.save(newRemindPushDO).getId();
        return getById(id);
    }

    @CacheEvict(value="remindPushCache", key="#id")
    @Transactional
    public RemindPushVO delete(Long id){
        RemindPushDO remindPushDO = remindPushDAO.findOne(id);
        if(remindPushDO == null){
            return new RemindPushVO();
        }

        remindPushDAO.delete(id);

        return new RemindPushVO();
    }

    @Override
    protected String getRedisKey() {
        return REMIND_KEY;
    }
}

*测试过程

调用http://127.0.0.1:8080/api/admin/remind/getById/21接口后,可以看到将key的类型java.lang.Number一起存入了客户端,再次访问接口,控制台不再打印sql语句,并且执行速度大大提高。
这里写图片描述
接口调用时间:
这里写图片描述
这里写图片描述
再次调用http://127.0.0.1:8080/api/admin/remind/getById/20接口,出现了两条数据
这里写图片描述
调用http://127.0.0.1:8080/api/admin/remind/modify接口修改id=21的数据,可以看到id=21的数据被清除掉了。
这里写图片描述

二、非注解缓存
注解缓存相比较而言,会麻烦一点,要手动处理缓存的使用,但是很灵活,也很容易协调各个方法的使用。

一种思路
因为findByIdfindAll(list)两类方法始终规避不掉在redis中要以两种形式存储,那么,可以这样做。
先定义findById(id)方法,先判断key=id的数据是否存在,存在则直接取出,不存在从数据库取出后再存入redis中;
然后,再定义findAll(list)方法,将获取到的list数据通过findById(id)方法一条一条存入redis,并且将所有数据的id拼接成1,2,3…的形式作为key=”list”的值存入redis,调用方法时,先判断key=”list”的数据是否存在,存在则取出数据1,2,3…,再调用findById(id)方法从缓存中取出一条条数据,不存在则再重复上述过程
并且在添加和删除操作中只需要增加或删除redis中key=id的数据后,再修改key=”list”的数据即可

package com.java.manager;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.java.DO.RemindPushDO;
import com.java.common.CommonRestResult;
import com.java.dao.RemindPushDAO;
import com.java.enums.StatusEnum;
import com.java.form.RemindPushForm;
import com.java.servive.redis.RedisService;
import com.java.vo.RemindPushVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class RemindPushManager extends RedisService {

    private static final String REMIND_KEY = "REMIND_KEY";

    @Autowired
    private RemindPushDAO remindPushDAO;

    public List list(String status){
        List remindPushDOList = null;
        String idStr = null;
        if(StringUtils.isNotBlank(idStr = get("list"))){
            List idList = Arrays.asList(idStr.split(","));
            return idList.stream().map(id -> {
               return  getById(Long.parseLong(id));
            }).collect(Collectors.toList());
        }

        List remindPushVOList = new ArrayList<>();

        remindPushDOList = remindPushDAO.getByStatus(status);
        StringBuilder sb = new StringBuilder();
        for(RemindPushDO remindPushDO : remindPushDOList){
            remindPushVOList.add(getById(remindPushDO.getId()));
            sb.append(remindPushDO.getId() + ",");
        }
        if(sb.length() > 0){
            sb.deleteCharAt(sb.length()-1);
        }
        put("list", sb.toString(), -1);
        return remindPushVOList;
    }

    public RemindPushVO getById(Long id){
        RemindPushDO remindPushDO = null;
        String remindPushDOStr = null;
        if(StringUtils.isNotBlank(remindPushDOStr = get(String.valueOf(id)))){
            remindPushDO = JSON.parseObject(remindPushDOStr, RemindPushDO.class);
        }else{
            remindPushDO = remindPushDAO.findOne(id);
            put(String.valueOf(id), JSON.toJSONString(remindPushDO), -1);
        }

        String str = JSON.toJSONString(remindPushDO);
        RemindPushVO remindPushVO = JSON.parseObject(str, RemindPushVO.class);
        return remindPushVO;
    }

    @Transactional
    public RemindPushVO create(RemindPushForm remindPushForm){
        String str = JSON.toJSONString(remindPushForm);
        RemindPushDO remindPushDO = JSON.parseObject(str, RemindPushDO.class);
        remindPushDO.setStatus(StatusEnum.NORMAL);
        remindPushDO.setCreateTime(new Date());
        remindPushDO.setUpdateTime(new Date());
        Long id = remindPushDAO.save(remindPushDO).getId();
        remindPushForm.setId(id);

        put(String.valueOf(id), JSON.toJSONString(remindPushDO), -1);
        String idStr = null;
        if(StringUtils.isNotBlank(idStr = get("list"))){
            String[] ids = idStr.split(",");
            List idList = new ArrayList<>(Arrays.asList(idStr.split(",")));
            if(!idList.contains(String.valueOf(id))){
                idList.add(String.valueOf(id));
                put("list", StringUtils.join(idList.toArray(), ","), -1);
            }
        }

        return getById(id);
    }


    @Transactional
    public RemindPushVO modify(RemindPushForm remindPushForm){
        RemindPushDO oldRemindPushDO = remindPushDAO.findOne(remindPushForm.getId());
        if(oldRemindPushDO == null){
            return new RemindPushVO();
        }

        String str = JSON.toJSONString(remindPushForm);
        RemindPushDO newRemindPushDO = JSON.parseObject(str, RemindPushDO.class);

        newRemindPushDO.setStatus(oldRemindPushDO.getStatus());
        newRemindPushDO.setCreateTime(oldRemindPushDO.getCreateTime());
        newRemindPushDO.setUpdateTime(new Date());
        Long id = remindPushDAO.save(newRemindPushDO).getId();

        put(String.valueOf(id), JSON.toJSONString(newRemindPushDO), -1);

        return getById(id);
    }

    @Transactional
    public RemindPushVO delete(Long id){
        RemindPushDO remindPushDO = remindPushDAO.findOne(id);
        if(remindPushDO == null){
            return new RemindPushVO();
        }

        remindPushDAO.delete(id);

        remove(String.valueOf(id));

        String idStr = null;
        if(StringUtils.isNotBlank(idStr = get("list"))){
            List idList = new ArrayList<>(Arrays.asList(idStr.split(",")));
            if(idList.contains(String.valueOf(id))){
                idList.remove(String.valueOf(id));
                put("list", StringUtils.join(idList.toArray(), ","), -1);
            }
        }

        return new RemindPushVO();
    }

    @Override
    protected String getRedisKey() {
        return REMIND_KEY;
    }
}

*测试过程
调用http://127.0.0.1:8080/api/admin/remind/list接口后,redis数据成功存入了findByIdfindAll(list)两种形式,再次调用接口后可以看到访问速度大大提高;之后再调用http://127.0.0.1:8080/api/admin/remind/getById/21接口将直接从缓存中取值;而调用http://127.0.0.1:8080/api/admin/remind/modify接口修改id=21数据的同时更新key=21的缓存,并不会影响到list缓存。
Spring Boot整合JPA+MySQL+Redis(二)_第1张图片
接口调用时间:
这里写图片描述
这里写图片描述

调用http://127.0.0.1:8080/api/admin/remind/delete/24接口后,redis数据变更,再次调用http://127.0.0.1:8080/api/admin/remind/list接口将依然从缓存中取值。
Spring Boot整合JPA+MySQL+Redis(二)_第2张图片

总结

全文讲述了Spring Boot整合JPA+MySQL+Redis的两种形式,包括注解形式和非注解形式,可以看到非注解形式明显更为灵活和可控,在redis存入过程中key的形式也更容易指定,但是稍显麻烦,不过成功掌握了从缓存中取单条数据和取多条数据的平衡点;而注解形式,笔者暂时找不到更好的解决方案来处理这个问题,读者如有建议,欢迎指点。

你可能感兴趣的:(spring-boot,redis,JPA)