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
四、執行結果