springboot实战(十三)之缓存

挑战者V      2022-04-17     436

关键词:

什么是缓存?

引用下百度百科的解释:

缓存就是数据交换的缓冲区(又称作Cache),当某一硬件要读取数据时,会首先从缓存中查找需要的数据,找到了则直接执行,找不到的话则从内存中查找。由于缓存的运行速度比内存快得多,故缓存的作用就是帮助硬件更快地运行。

因为缓存往往使用的是RAM(断电即掉的非永久性储存),所以在用完后还是会把文件送到硬盘存储器里永久存储。电脑里最大的缓存就是内存条了,最快的是CPU上镶的L1和L2缓存,显卡的显存是给显卡运算芯片用的缓存,硬盘上也有16M或者32M的缓存。

 

说到这你或许很疑问为什么要用缓存?

比如面对千万级数据时,对于并发量和性能是非常具有挑战性的。这时如果不采用缓存的话,你每次都要直接去数据库查,那么数据库即便是分库分表,对于性能而言也是一笔不小的开支。说到这你也许还不明白为什么用缓存。直白的讲,将你每次需要到数据库中获取的大批量数据,缓存起来,每次需要查询对应的数据时,直接去缓存里面查。当然了,这里可能还会存在一个比较大的问题,对于部分项目而言,比如广告投放项目或者是一些电商项目,数据变动相对比较大,这样会导致一个问题,就是缓存数据的实时性。这里后续会讲。今天主要讲的是SpringBoot作缓存的简单Demo,主要面向一些初学者,同时笔者也作一格小小记录。框架越往后发展,就越轻量级。想当初,搭建SSM框架,一大堆XML先不说(只要很好的管理起来,看起来也不是那么讨厌),最让人头痛的就是每次引用一些非关系型数据库或者是一些类库都要导入对应的maven依赖,这是一件很头痛的事情,因为有些时候,一些依赖之间,它们会存在一定的冲突。不过还好用maven作为依赖管理,处理冲突问题还是很不错。想到我的一位朋友,他公司用的还是动态web项目。也就是手动导入jar包,有的时候还得build path一下,想到这,我觉得还是很幸运。说的或许有些偏题了,不过最想说还是,感谢老外开发出来这个SpringBoot,因为这样让我们的开发效率更加快了。

不过,就国内而言,虽然也不乏有人研究SpringBoot源码,开发出对应的开发项目,比如JFinal或者是Jeecg,但是在应用方面的广度仍不及老外,离老外还是有一定的差距,不过让我高兴的是,这个差距不再是望尘莫及,而是望其项背。话不多说,源码贴起。

 

一、导入Maven依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>cn.yc.springboot.cache</groupId>
  <artifactId>SprintBoot-Cache</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.30</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

 

二、执行SQL脚本

DROP TABLE IF EXISTS `article`;

CREATE TABLE `article` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(50) DEFAULT NULL,
  `author` varchar(50) DEFAULT NULL,
  `content` text,
  `file_name` varchar(255) DEFAULT NULL,
  `state` int(2) DEFAULT '1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

/*Data for the table `article` */

insert  into `article`(`id`,`title`,`author`,`content`,`file_name`,`state`) values (1,'三国演义','罗贯中','test1324fdsafadsfadsfa','test001',1),(2,'水浒城','施耐庵','官逼民反','test002',1);

 

三、编写对应的类(entity,dao,service及其controller和model、启动类)

SpringbootCacheApplication.java

package com.blog.controller;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableAutoConfiguration
@EnableCaching
public class SpringbootCacheApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootCacheApplication.class, args);
    }

}

 

Article.java

package com.blog.entity;

import java.io.Serializable;


public class Article implements Serializable {

    private Integer id;

    private String title;

    private String content;

    private String author;

    private String fileName;

    private Integer state;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public Integer getState() {
        return state;
    }

    public void setState(Integer state) {
        this.state = state;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }


}

 

ArticleMapper.java

package com.blog.dao;


import org.apache.ibatis.annotations.Param;

import com.blog.entity.Article;


public interface ArticleMapper {


    /**
     * 插入一篇文章
     * @param title
     * @param author
     * @param content
     * @param fileName
     * @return
     */
    public Integer addArticle(@Param("title") String  title,@Param("author")String author,
                              @Param("content")String content,@Param("fileName")String fileName);
    /**
     * 根据id获取文章
     * @param id
     * @return
     */
    public Article getArticleById(@Param("id") Integer id);

    /**
     * 更新content
     * @param content
     */
    public Integer updateContentById(@Param("content")String content,@Param("id")Integer id);

    /**
     * 根据id删除文章
     * @param id
     * @return
     */
    public Integer removeArticleById(@Param("id")Integer id);

    /**
     * 获得上一次插入的id
     * @return
     */
    public Integer getLastInertId();

}

 

ArticleService.java

package com.blog.service;


import java.util.concurrent.atomic.AtomicInteger;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import com.blog.dao.ArticleMapper;
import com.blog.entity.Article;


@Service
@CacheConfig(cacheNames = "articleCache")
public class ArticleService {

    private AtomicInteger count =new AtomicInteger(0);

    @Autowired
    private ArticleMapper articleMapper;


    /**
     * 增加一篇文章
     * @return
     */
    @CachePut()
    public Integer addArticle(Article article){
        Integer result = articleMapper.addArticle(article.getTitle(), article.getAuthor(), article.getContent(), article.getFileName());
        if (result>0) {
            Integer lastInertId = articleMapper.getLastInertId();
            System.out.println("--执行增加操作--id:" + lastInertId);
        }
        return result;
    }

    /**
     * 获取文章
     * @param id 文章id
     * @return
     */
    @Cacheable(key = "#id",unless = "#result.state==0")
    public Article getArticle(Integer id) {
        try {
            //模拟耗时操作
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        final Article artcile = articleMapper.getArticleById(id);
        System.out.println("--执行数据库查询操作"+count.incrementAndGet()+"次"+"id:"+id);
        return artcile;
    }

    /**
     * 通过id更新内容
     *
     * @param id
     * @return
     */
    @CacheEvict(key = "#id")
    public Integer updateContentById(String contetnt, Integer id) {
        Integer result = articleMapper.updateContentById(contetnt, id);
        System.out.println("--执行更新操作id:--"+id);
        return result;
    }

    /**
     * 通过id移除文章
     * @param id
     * @return
     */
    @CacheEvict(key = "#id")
    public Integer removeArticleById(Integer id){
        final Integer result = articleMapper.removeArticleById(id);
        System.out.println("执行删除操作,id:"+id);
        return result;
    }

}

 

ArticleController.java

package com.blog.controller;


import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.blog.dao.ArticleMapper;
import com.blog.entity.Article;
import com.blog.model.ResultVo;
import com.blog.service.ArticleService;


@RestController
@ComponentScan(basePackages = {"com.blog.controller", "com.blog.service"})
@MapperScan(basePackages = {"com.blog.dao"})
public class ArticleController {

    @Autowired
    private ArticleService articleService;

    @Autowired
    ArticleMapper articleMapper;

    @PostMapping("/add")
    public ResultVo addArticle(@RequestBody Article article) {

        System.out.println(article.toString());
        Integer result = articleService.addArticle(article);

        if (result >= 0) {
            return ResultVo.success(result);
        }
        return ResultVo.fail();
    }


    @GetMapping("/get")
    public ResultVo getArticle(@RequestParam("id") Integer id) {

        Article article = articleService.getArticle(id);

        if (null != article)
            return ResultVo.success(article);
        return ResultVo.fail();
    }


    /**
     * 更新一篇文章
     *
     * @param contetnt
     * @param id
     * @return
     */
    @GetMapping("/resh")
    public ResultVo update(@RequestParam("content") String contetnt, @RequestParam("id") Integer id) {
        final Integer result = articleService.updateContentById(contetnt, id);
        if (result > 0) {
            return ResultVo.success(result);
        } else {
            return ResultVo.fail();
        }
    }

    /**
     * 删除一篇文章
     *
     * @param id
     * @return
     */
    @GetMapping("/rem")
    public ResultVo remove(@RequestParam("id") Integer id) {

        final Integer result = articleService.removeArticleById(id);
        if (result > 0) {
            return ResultVo.success(result);
        } else {
            return ResultVo.fail();
        }
    }

}

 

ResultVo.java

package com.blog.model;

import java.io.Serializable;

public class ResultVo<T> implements Serializable {

    private T data;
    private Integer code;
    private String msg;

    public static final String errorMsg = "操作失败";

    public static final String successMsg = "操作成功";

    public ResultVo(T data) {
        this.data = data;
    }

    public ResultVo(T data, Integer code) {
        this.data = data;
        this.code = code;
    }

    public ResultVo(T data, Integer code, String msg) {
        this.data = data;
        this.code = code;
        this.msg = msg;
    }

    public ResultVo(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    /**
     * 成功
     *
     * @param <T> 数据
     * @return
     */
    public static <T> ResultVo success(T data) {
        return new ResultVo<T>(data, 1, successMsg);
    }

    /**
     * 失败
     * @param <T>
     * @return
     */
    public static <T> ResultVo fail() {
        return new ResultVo(0, errorMsg);
    }


    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

 

四、编写XML及其对应配置文件

ArticleMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.blog.dao.ArticleMapper">

    <resultMap type="com.blog.entity.Article" id="articleMap">
        <result column="id" property="id"/>
        <result column="title" property="title"/>
        <result column="author" property="author"/>
        <result column="content" property="content"/>
        <result column="file_name" property="fileName"/>
        <result column="state" property="state"></result>
    </resultMap>

    <insert id="addArticle">
        INSERT INTO  article (title,author,content,file_name,state) values
        (#{title}, #{author}, #{content},#{fileName},'1')
    </insert>


    <select id="getArticleById" resultMap="articleMap">
        select * from article where id = #{id}
    </select>


    <update id="updateContentById">
        update article set content = #{content} where id = #{id}
    </update>

    <update id="removeArticleById">
        update article set state = '0' where id = #{id}
    </update>

    <select id="getLastInertId" resultType="java.lang.Integer">
        select LAST_INSERT_ID()
    </select>


</mapper>

 

application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=1234
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
mybatis.mapperLocations=classpath*:mapper/*.xml 

 

四、运行结果

 

(十三)atp应用测试平台——springboot集成kafka案例实战(代码片段)

...:①削峰填谷②异步解耦。本节我们主要介绍一下如何在springboot项目中集成kafka消息中间键,实现简单的数据分发以及消费的案例。正文kafka集群搭建快速搭建一个kafka集群,我们这里以docker 查看详情

企业级springboot教程(十三)springboot集成springcache

本文介绍如何在springboot中使用默认的springcache,声明式缓存Spring定义CacheManager和Cache接口用来统一不同的缓存技术。例如JCache、EhCache、Hazelcast、Guava、Redis等。在使用Spring集成Cache的时候,我们需要注册实现的CacheManager的Bean。Sprin... 查看详情

企业级springboot教程(十三)springboot集成springcache

本文介绍如何在springboot中使用默认的springcache,声明式缓存Spring定义CacheManager和Cache接口用来统一不同的缓存技术。例如JCache、EhCache、Hazelcast、Guava、Redis等。在使用Spring集成Cache的时候,我们需要注册实现的CacheManager的Bean。Sprin... 查看详情

mp实战系列(十三)之批量修改操作(前后台异步交互)(代码片段)

MyBatis的批量操作其实同MyBatis基本是一样的。并无多大区别,要说区别,除了封装的方法之外,主要就是注解方面的区别,比如@TableId、@TableField、@TableName等等区别。示例描述:本次描述的是批量相关的操作,主要是批量修改等操... 查看详情

springboot下的redis缓存实战

最近在做的一个系统涉及到基础数据的频繁调用,大量的网络开销和数据读写给系统带来了极大的性能压力,我们决定引入缓存机制来缓解系统压力。什么是缓存提起缓存机制,大概10个程序员总有5种不同的解释吧(姑且认为只... 查看详情

springboot整合rabbitmq之发送接收消息实战

实战前言前几篇文章中,我们介绍了SpringBoot整合RabbitMQ的配置以及实战了Spring的事件驱动模型,这两篇文章对于我们后续实战RabbitMQ其他知识要点将起到奠基的作用的。特别是Spring的事件驱动模型,当我们全篇实战完毕RabbitMQ并... 查看详情

springboot之缓存

一、准备工作首先整合使用Spring整合MyBatis。可参阅:SpringBoot整合MyBatis SpringBoot整合MyBatis完后后,我们需要在pom.xml中添加缓存相关的依赖。<!--cache--><dependency><groupId>org.springframework.boot</groupId><ar 查看详情

springboot实战之thymeleaf

...共同的职能是MVC模式中的视图展示层,即View。当然了,SpringBoot中也可以用jsp,不过不推荐这种用法,比较推崇的就是使用Thymeleaf。关于Thymeleaf学习,建议参考官方文档:https://www.thymeleaf.org/documentation.html官方文档例子,应有尽有。... 查看详情

重学springboot系列之redis与springcache缓存(代码片段)

重学SpringBoot系列之redis缓存使用docker安装redis准备工作获取redis镜像创建容器创建持久化存储目录获取redis的默认配置文件模版使用镜像创建一个容器查看活跃的容器访问redis容器服务开启防火墙端口,提供外部访问redis数据结... 查看详情

springboot实战之url传参

参考技术A其中required=false是说明这个字段可以传入也可以不传入,如果不这样写,就会匹配version字段,匹配不到,就会报错,这时,如果访问"/index"路径也会报错。【注意】要点击@PathVariable注解进去看一下,是否支持required参数... 查看详情

微服务springboot整合redis实战开发解决高并发数据缓存

一、什么是缓存?缓存(Cache),就是数据交换的缓冲区,俗称的缓存就是缓冲区内的数据,一般从数据库中获取,存储于本地代码,例如:例1:StaticfinalConcurrentHashMap<K,V>map=newConcurrentHashMap<>();本地用于高并发例2:staticfinalCache<K,... 查看详情

springboot实战之使用restfulweb服务

一、导入maven依赖<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.or 查看详情

springboot+vue+实战项目之第1集(代码片段)

SpringBoot+vue+实战项目--锋迷商城1.项目功能2.技术选型3.项目架构的演进4.SpringBoot5.Usercontroller(实操注册用户)6.Java配置方式6.1xml配置6.2注解配置6.3Java配置⽅式(第三方类对象)7.SpringBoot自动配置(底层... 查看详情

springboot2.x实战之定时任务调度

...务的,例如:定时同步一批数据、定时清理一些数据,在SpringBoot中提供了@Scheduled注解就提供了定时调度的功能,对于简单的、单机的调度方案是足够了的。这篇文章准备用实际案例看下@Scheduled的用法。开发实战新建SpringBoot工... 查看详情

springboot2.x实战之定时任务调度

...务的,例如:定时同步一批数据、定时清理一些数据,在SpringBoot中提供了@Scheduled注解就提供了定时调度的功能,对于简单的、单机的调度方案是足够了的。这篇文章准备用实际案例看下@Scheduled的用法。开发实战新建SpringBoot工... 查看详情

javacv的摄像头实战之十三:年龄检测(代码片段)

...ps://github.com/zq2599/blog_demos本篇概览本文是《JavaCV的摄像头实战》系列的第十三篇,前文《JavaCV的摄像头实战之十二:性别检测》中,借助训练好的卷积神经网络模型开发出了识别性别的应用,今天在前文基础上做... 查看详情

shiro整合springboot缓存之redis实现(代码片段)

Shiro整合Springboot缓存之Redis实现Redis下载安装引入redis依赖配置redis连接启动redis服务自定义redis缓存的实现自定义shiro缓存管理器自定义salt实现实现序列化接口自定义Realm改造开启redis缓存Redis下载安装Windows系统中Redis下载安装其它... 查看详情

从零开发短视频电商缓存cache实战simplecaffeine和redis多缓存管理器(代码片段)

文章目录SpringBoot集成缓存Cache1.增加pom依赖2.启用缓存功能常见缓存操作缓存清除缓存更新缓存组合缓存类缓存配置SpEL上下文数据Cache实现之Redis缓存管理器方式一RedisCacheConfiguration方式二RedisCacheManagerBuilderCustomizer方式三CachingConfi... 查看详情