SpringBoot实战(十三)之缓存

什么是缓存?

引用下百度百科的解释:

缓存就是数据交换的缓冲区(又称作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 

 

四、运行结果