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

【Java项目脚手架系列】第六篇:Spring Boot + JPA项目脚手架 Spring Boot 的快速开发能力JPA 的对象关系映射完整的数据库操作支持事务管理能力测试框架支持Spring Boot + JPA 脚手架提供了一个完整的企业级应用开发基础,包含了必要的配置和示例代码。快速搭建 Web 应用实现数据库操作进行单元测试使用开发工具。 阅读详情

前言

在之前的发布的博客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<String> {

    private static final String REMIND_KEY = "REMIND_KEY";

    @Autowired
    private RemindPushDAO remindPushDAO;

    public List<RemindPushVO> 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<String> {

    private static final String REMIND_KEY = "REMIND_KEY";

    @Autowired
    private RemindPushDAO remindPushDAO;

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

        List<RemindPushVO> 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<String> 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<String> 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缓存。
这里写图片描述
接口调用时间:
这里写图片描述
这里写图片描述

调用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的两种形式,包括注解形式和非注解形式,可以看到非注解形式明显更为灵活和可控,在redis存入过程中key的形式也更容易指定,但是稍显麻烦,不过成功掌握了从缓存中取单条数据和取多条数据的平衡点;而注解形式,笔者暂时找不到更好的解决方案来处理这个问题,读者如有建议,欢迎指点。

第 5-8 课:Spring Boot 整合 Spring Data JPA 前言 上节课我们学习了 Spring Boot 整合 MyBatis 的具体操作,MyBatis 是一款优秀的持久层框架,可以帮助开发者简化代码开发,从而提高工作效率。作为行业 Java 开发的行业标准,功能强大的 Spring 当然也提供了持久层解决方案,它就是 Spring Data。 Spring Data 又根据具体的数据库分为不同的子模块,无论是关系型数据库还是非关系型数据库,Sprin... 阅读详情

相关推荐

SpringBoot+JPA+MySQL+Swagger+Redis实现CRUD

通过SpringBoot+JPA+MySQL+Swagger+Redis实现CRUD,对数据库进行增删改查,分页查找的接口文档

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

Spring Boot整合JPA+MySQL+Redis 在开始之前,我们在pom.xml添入关键依赖: JPA依赖: dependency&gt; groupId&gt;org.springframework.bootgroupId&gt; artifactId&gt;spring-boot-starter-data-jpaartifactId&gt; dependency&gt; MySQL依赖:

IceCaptain的博客 6677

SpringBoot+Redis+JPA +IDEA中的简单操作数据库逆向生成

适合新手 学习IDEA

整合SpringBoot+JPA+Mysql+Redis实现缓存机制的一个Demo

公司的项目中用到了Redis,最近一直在学习这一块。从开始学习到现在,总体给我的感觉是Redis上手比较容易,但是精通需要大量的实践和研究的。好在我们公司对Redis的使用并不是那么深入,目前只进行些简单的操作。总结这几天的学习结果,记录下来,希望有助于巩固和提高。 说明:我的Redis服务是搭建在虚拟机Linux(Read Heat6.2)系统上的,Mysql数据库安装在本机,虚拟机的...

CQHaveADream的博客 3292

springboot整合mybatis,mysql做数据库存储,redis做缓存

redis应用的场景 通过缓存来减少对关系型数据库的查询次数,减轻数据库压力。在执行DAO类的select***(), query***()方法时,先从Redis中查询有没有缓存数据,如果有则直接从Redis拿到结果,如果没有再向数据库发起查询请求取数据。   springboot已经集成了redis缓存,只需要在pom.xml中加载redis,然后通过注解即可完成配置。 首先在配置类...

wanderlustLee的博客 1万+

优雅使用函数式编程实现先查redis,再查数据库

优雅的实现了先查redis再查数据库,以后根据业务需求,自己传入相应的业务查询方法,和各种参数值即可!新手小白,还望指教!

leyasuzhou的博客 1104

SpringBoot+Redis作为级缓存整合

SpringBoot+Redis作为级缓存整合

今晚摸鱼 4235

Spring Boot 整合 Spring Data JPA、strategy 的策略区别、什么是 Spring Data JPA

Spring Boot 整合 Spring Data JPA、strategy 的策略区别、什么是 Spring Data JPA

hutao8896的博客 1104

Spring Boot中集成RedisMySQL

用途:将键或简单的值序列化为字符串。适用场景:通常用于键的序列化,确保键在 Redis 中以字符串存储,以便于直接查看和管理。用途:将对象序列化为 JSON 格式的字符串,并支持 JSON 反序列化回对象。适用场景:通常用于值的序列化,尤其是需要存储复杂对象的情况。它可以确保数据的可读性,且 JSON 格式数据跨系统兼容性好。缓存穿透指的是请求的数据在缓存和数据库中都不存在,这种请求直接穿过缓存访问数据库。当恶意请求大量涌入时,缓存层无法拦截这些无效请求,导致数据库承受大量压力,从而影响性能。

m0_73837751的博客 3240

Spring boot整合Spring Data JpaRedisSpring Cache入门练习

这两天一直在学习Spring Cache,下面就简单总结一下。 首先,从创建项目开始:我使用的是Spring tool suit创建的Spring boot + Spring Data Jpa 项目,这里就不详细讲了。项目简单整合RedisSpring Cache。 下面是主配置文件application.properties的配置: spring.datasource.url = jd...

weixin_42904666的博客 1327

Spring Boot数据访问,整合MyBatis,JPA,Redis

数据准备,在MySQL中建一个名springbootdata的数据库 # 创建数据库 CREATE DATABASE springbootdata; # 选择使用数据库 USE springbootdata; # 创建表t_article并插入相关数据 DROP TABLE IF EXISTS `t_article`; CREATE TABLE `t_article` ( `id` int(20) NOT NULL AUTO_INCREMENT COMMENT '文章id', `title` va.

weixin_46083166的博客 479

Spring Boot 实践之六 Spring Boot数据访问(Sping Boot整合MyBatis/Sping Boot整合JPA/Sping Boot整合Redis)

​ 在开发中,通常会涉及到对数据库的数据进行操作,Spring Boot在简化项目开发以及实现自动化配置的基础上,对关系型数据库和非关系型数据库的访问操作都提供了非常好的整合支持。 ​ 本章,将对Spring Boot的数据访问进行介绍,并对常用的数据操作框架进行整合讲解。 3.1 Spring Boot数据访问概述 Spring Boot默认采用整合SpringDa...

qq_26665293的博客 2964

Spring Boot整合Redis实现简单的分布式锁

随着微服务等分布式架构的快速发展及应用,在很多情况下,我们都会遇到在并发情况下多个线程竞争资源的情况,比如我们耳熟能详的秒杀活动,多平台多用户对同一个资源进行操作等场景等。分布式锁的实现方式有很多种,比如基于数据库、Zookeeper、Redis等,本文我们主要介绍Spring Boot整合Redis实现分布式锁。

Ron.Zheng 1万+

SpringBoot2 jpa级缓冲配置

maven的pom文件配置 &lt;dependencies&gt; &lt;dependency&gt; &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt; &lt;artifactId&gt;spring-boot-starter-data-jpa&lt;/arti...

学习和工作总结,共勉! 3878

Spring DataJPA Redis教程(DataJPA实战版)

本教程主要详细讲解Spring Data Redis,它向Redis提供Spring Data平台的抽象.Redis由基于key/value库的数据结构存数,以持久保存数据,并可用作数据库,缓存,消息代理等。

qianmoQ - 关注云计算,关注大数据 1003

整合常用技术框架之 JPARedis

整合 Spring Data JPA 首先,添加依赖,代码如下: &lt;dependency&gt; &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt; &lt;artifactId&gt;spring-boot-starter-data-jpa&lt;/artifactId&gt; &lt;/dependency&gt; &lt...

qq_41377914的博客 1681

SpringBoot系列教材 (十四)- 其他 - springboot jpa redis 整合在一起做一套带分页的 增删改查 crud

如果用这种方式,就会导致数据不同步,即,虽然增加了,并且也增加到缓存中了,但是因为 key 不一样,通过查询拿到的数据,这样会到在,在增加数据之后,就会在Redis 中以 key= "category 71" 缓存一条数据。这个道理和 add 是一样的,仅仅删除 key= "category 71" ,没有什么意义, key: "category 0-5" 里面的数据没有影响呀。所以没有撸前面的同学,请先撸了,不然这里也撸不出来,因为数据库怎么建立都是在前面写的,你没做,后面也没法做的。

Lan_ht的博客 196

springBoot整合jparedis缓存

**说说JPA——JPA是一组规范,并不是一个框架。hibernate是实现JPA最好的一个框架,框架使用JPA对数据库的交互进行了封装,,所以我下面的案例中就是用hibernate框架进行融合展示的。 说说redis——redis也是相当于数据库,不过传统的数据库数据是存储在硬盘中的,而redis可以将数据存储在内存中,书写的速度很快,所以它被用于缓存当中,使用redis缓存后操作数据库得到数据...

qq_43734394的博客 820
上一篇: ActiveMQ消息队列demo
下一篇: 涂鸦智能Java实习面试经历
IceCaptain
博客等级 码龄9年 12粉丝 19原创
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值