Spring Boot - 缓存
发布日期:2021-05-06 23:02:10 浏览次数:28 分类:精选文章

本文共 15588 字,大约阅读时间需要 51 分钟。

文章目录

准备

IDE

Spring Tool Suite 4Version: 4.4.0.RELEASE

JDK

$ java -versionjava version "1.8.0_241"Java(TM) SE Runtime Environment (build 1.8.0_241-b07)Java HotSpot(TM) 64-Bit Server VM (build 25.241-b07, mixed mode)

MySQL 数据库

建表语句以及预设数据:

CREATE TABLE `commodity` (  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '商品标识符',  `name` varchar(16) NOT NULL COMMENT '商品名称',  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';INSERT INTO `commodity` (id, name) VALUES (DEFAULT, '李子');INSERT INTO `commodity` (id, name) VALUES (DEFAULT, '栗子');INSERT INTO `commodity` (id, name) VALUES (DEFAULT, '梨子');

项目搭建

新建 Spring Starter Project:

在这里插入图片描述

在这里插入图片描述

修改 pom.xml 文件,引入 Druid、MyBatis Plus 等依赖:

4.0.0
org.springframework.boot
spring-boot-starter-parent
2.3.4.RELEASE
com.mk
spring-boot-cache
1.0.0
spring-boot-cache
缓存
1.8
1.1.21
3.3.1.tmp
org.springframework.boot
spring-boot-starter-cache
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-devtools
runtime
true
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-configuration-processor
true
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
com.alibaba
druid-spring-boot-starter
${druid-spring-boot-starter.version}
com.baomidou
mybatis-plus-boot-starter
${mybatis-plus-boot-starter.version}
org.springframework.boot
spring-boot-maven-plugin

新建 application.yml 文件,配置 Druid、MyBatis-Plus 等参数:

server:  port: 8080  servlet:    session:      timeout: 12hspring:  datasource:    druid:      driver-class-name: com.mysql.cj.jdbc.Driver      url: jdbc:mysql://192.168.88.158:3306/spring-boot?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=true      username: root      password: 123456      initial-size: 5      min-idle: 5      max-active: 20      max-wait: 30000 # 获取连接等待超时的时间      time-between-eviction-runs-millis: 2000 # 间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒      min-evictable-idle-time-millis: 600000 # 一个连接在池中最小生存的时间,单位是毫秒      max-evictable-idle-time-millis: 900000      validation-query: select '1'      test-while-idle: true      test-on-borrow: false      test-on-return: false      pool-prepared-statements: true      max-open-prepared-statements: 20      max-pool-prepared-statement-per-connection-size: 20      filters: stat,wall,slf4j      web-stat-filter: # WebStatFilter 配置        enabled: true        url-pattern: /*        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'      stat-view-servlet: # StatViewServlet 配置        enabled: true        url-pattern: /druid/*        reset-enable: false        login-username: admin        login-password: 123456#        allow: 127.0.0.1#        deny: 192.168.1.6#      aop-patterns: # Spring 监控配置#      - com.mk.service.*      filter:        stat: # 慢 SQL 记录          slow-sql-millis: 3000          log-slow-sql: truelogging:  level:    com.mk: DEBUGmybatis-plus:#  mapper-locations:#  - classpath:mapper/*Mapper.xml#  global-config:#    db-config:#      id-type: AUTO # 数据库的表的主键生成策略  configuration:    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl # 日志  type-aliases-package: com.mk.pojo

新建 MyBatis-Plus 配置类:

package com.mk.configuration;import org.mybatis.spring.annotation.MapperScan;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.transaction.annotation.EnableTransactionManagement;import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;@Configuration@MapperScan(basePackages = {    "com.mk.mapper" }) // 扫描 Mapper 接口@EnableTransactionManagement // 开启事务管理public class MyBatisPlusConfiguration {       /**     * 

分页拦截器 * @return */ @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); }}

启动类:

package com.mk;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class SpringBootCacheApplication {       public static void main(String[] args) {           SpringApplication.run(SpringBootCacheApplication.class, args);    }}

商品表实体类:

package com.mk.pojo;import java.io.Serializable;import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.annotation.TableId;import com.baomidou.mybatisplus.extension.activerecord.Model;import lombok.Data;import lombok.EqualsAndHashCode;import lombok.experimental.Accessors;@Data@EqualsAndHashCode(callSuper = false)@Accessors(chain = true)public class Commodity extends Model
{ private static final long serialVersionUID=1L; /** * 商品标识符 */ @TableId(value = "id", type = IdType.AUTO) private Integer id; /** * 商品名称 */ private String name; /** * 创建时间 */ private String createTime; /** * 更新时间 */ private String updateTime; @Override protected Serializable pkVal() { return this.id; }}

商品表 Mapper 接口及其映射文件:

package com.mk.mapper;import com.mk.pojo.Commodity;import com.baomidou.mybatisplus.core.mapper.BaseMapper;public interface CommodityMapper extends BaseMapper
{ }
id, name, create_time, update_time

商品表服务接口及其实现类:

package com.mk.service;import com.mk.pojo.Commodity;import com.baomidou.mybatisplus.extension.service.IService;public interface CommodityService extends IService
{ }
package com.mk.service.impl;import com.mk.pojo.Commodity;import com.mk.mapper.CommodityMapper;import com.mk.service.CommodityService;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import org.springframework.stereotype.Service;@Servicepublic class CommodityServiceImpl extends ServiceImpl
implements CommodityService { }

商品表前端控制器:

package com.mk.controller;import java.time.LocalDateTime;import java.util.HashMap;import java.util.Map;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import com.mk.service.CommodityService;@RestController@RequestMapping("/commodity")public class CommodityController {       @Autowired    private CommodityService commodityService;        /**     * 

根据标识符查询 * @param id * @return */ @GetMapping("/{id}") public Map

getById(@PathVariable("id") Integer id) { Map
result = new HashMap<>(); result.put("datetime", LocalDateTime.now()); result.put("data", this.commodityService.getById(id)); return result; }}

运行启动类,访问 ,若能返回预期的数据即可:

在这里插入图片描述

使用 @Cacheable 缓存数据

修改商品服务实现类,重写 getById 方法,添加 @Cacheable 注解,实现查询缓存或缓存数据:

// ... 其他,略import org.springframework.cache.annotation.Cacheable;import org.springframework.stereotype.Service;@Servicepublic class CommodityServiceImpl extends ServiceImpl
implements CommodityService { @Override @Cacheable(cacheNames = "commodity", key = "#id") // cacheNames 指定缓存容器的名称,一个缓存容器中可以有多条缓存数据。key 指定缓存数据的键值。 public Commodity getById(Serializable id) { return super.getById(id); }}

重启应用,清空控制台输出,多次访问 http://localhost:8080/commodity/1,观察控制台输出,可以看到只有第一次访问是查询了数据库,随后几次都没有,说明随后的查询是直接从缓存中获取数据,而非数据库:

2020-09-20 16:10:04.404 DEBUG 10644 --- [nio-8080-exec-1] c.mk.mapper.CommodityMapper.selectById   : ==>  Preparing: SELECT id,name,create_time,update_time FROM commodity WHERE id=? 2020-09-20 16:10:04.405 DEBUG 10644 --- [nio-8080-exec-1] c.mk.mapper.CommodityMapper.selectById   : ==> Parameters: 1(Integer)2020-09-20 16:10:04.407 DEBUG 10644 --- [nio-8080-exec-1] c.mk.mapper.CommodityMapper.selectById   : <==      Total: 1

底层配置与实现(暂不分析)

线索:CacheAutoConfigurationSimpleCacheConfiguration

使用 @CachePut 更新缓存数据

在没有使用 @CachePut 的情况下更新数据库中的数据,是不会同步更新到缓存中的。如果要实现同步更新到缓存,就需要使用 @CachePut

修改商品服务接口,添加 updateById 方法:

public interface CommodityService extends IService
{ /** *

根据标识符更新 * @param id * @param entity * @return * @throws Exception */ Commodity updateById(Serializable id, Commodity entity) throws Exception;}

修改商品服务实现类,实现 updateById 方法:

// ... 其他,略import org.springframework.cache.annotation.CachePut;import org.springframework.cache.annotation.Cacheable;import org.springframework.stereotype.Service;@Servicepublic class CommodityServiceImpl extends ServiceImpl
implements CommodityService { // ... 其他,略 @Override @CachePut(cacheNames = "commodity", key = "#id") // @CachePut 在方法执行完成之后起作用,其使用方法的返回值做为缓存数据,key 指定更新缓存容器中的那一条数据。 public Commodity updateById(Serializable id, Commodity entity) throws Exception { entity.setId((Integer) id); boolean result = super.updateById(entity); Commodity record = null; if (result) { record = super.getById(id); } else { throw new Exception("更新失败"); } return record; }}

修改商品前端控制器类,添加 updateById 方法:

// ... 其他,略@RestController@RequestMapping("/commodity")public class CommodityController {       // ... 其他,略        /**     * 

根据标识符更新数据 * @param entity * @return */ @PostMapping("") public Map

updateById(Commodity entity) { Map
result = new HashMap<>(); result.put("datetime", LocalDateTime.now()); try { result.put("data", this.commodityService.updateById(entity.getId(), entity)); } catch (Exception e) { result.put("message", e.getMessage()); } return result; }}

重启应用,使用 Postman 向 发起一个 POST 请求,更新 id = 1 的数据:

在这里插入图片描述

更新成功之后,数据库表中的数据:

mysql> select * from commodity where id = 1;+----+--------+---------------------+---------------------+| id | name   | create_time         | update_time         |+----+--------+---------------------+---------------------+|  1 | 苹果   | 2020-09-20 02:37:59 | 2020-09-20 09:50:30 |+----+--------+---------------------+---------------------+1 row in set (0.00 sec)

控制台返回的数据:

2020-09-20 17:50:46.070 DEBUG 11452 --- [nio-8080-exec-8] c.mk.mapper.CommodityMapper.updateById   : ==>  Preparing: UPDATE commodity SET name=? WHERE id=? 2020-09-20 17:50:46.071 DEBUG 11452 --- [nio-8080-exec-8] c.mk.mapper.CommodityMapper.updateById   : ==> Parameters: 苹果(String), 1(Integer)2020-09-20 17:50:46.074 DEBUG 11452 --- [nio-8080-exec-8] c.mk.mapper.CommodityMapper.updateById   : <==    Updates: 12020-09-20 17:50:46.075 DEBUG 11452 --- [nio-8080-exec-8] c.mk.mapper.CommodityMapper.selectById   : ==>  Preparing: SELECT id,name,create_time,update_time FROM commodity WHERE id=? 2020-09-20 17:50:46.076 DEBUG 11452 --- [nio-8080-exec-8] c.mk.mapper.CommodityMapper.selectById   : ==> Parameters: 1(Integer)2020-09-20 17:50:46.079 DEBUG 11452 --- [nio-8080-exec-8] c.mk.mapper.CommodityMapper.selectById   : <==      Total: 1

完成以上操作之后,清空控制台信息,访问 ,查询 id = 1 的数据:

在这里插入图片描述

观察控制台的输出,可以发现,控制台并没有任何输出,说明数据来自缓存,并且该数据与更新结果一致,说明更新的数据同步到缓存中。

使用 @CacheEvict 删除缓存数据

在没有使用 @CacheEvict 的情况下删除数据库中的数据,是不会同步删除缓存数据。如果要实现同步删除缓存数据,就需要使用 @CacheEvict

修改商品服务实现类,重写 removeById 方法,添加 @CacheEvict 注解,指定缓存容器和将要删除的缓存数据的键值:

// ... 其他,略import org.springframework.cache.annotation.CacheEvict;@Servicepublic class CommodityServiceImpl extends ServiceImpl
implements CommodityService { // ... 其他,略 @Override @CacheEvict(cacheNames = "commodity", key = "#id") public boolean removeById(Serializable id) { return super.removeById(id); }}

修改商品前端控制器,添加 deleteById 方法:

// ... 其他,略@RestController@RequestMapping("/commodity")public class CommodityController {       @Autowired    private CommodityService commodityService;        // ... 其他,略        /**     * 

根据标识符删除 * @return */ @DeleteMapping("/{id}") public Map

deleteById(@PathVariable("id") Integer id) { Map
result = new HashMap<>(); result.put("datetime", LocalDateTime.now()); result.put("data", this.commodityService.removeById(id)); return result; }}

重启应用,使用浏览器访问 ,使 id = 1 的数据被缓存。

使用 Postman 向 发起一个 DELETE 请求,删除 id = 1 的数据:

在这里插入图片描述

再次使用浏览器访问 ,可以看到返回的数据为空:

在这里插入图片描述

观察控制台输出的信息,可以看到 Spring Boot 在数据库查询数据,说明缓存中的数据已经被删除了。

2020-09-20 21:09:34.752 DEBUG 4824 --- [nio-8080-exec-8] c.mk.mapper.CommodityMapper.selectById   : ==>  Preparing: SELECT id,name,create_time,update_time FROM commodity WHERE id=? 2020-09-20 21:09:34.752 DEBUG 4824 --- [nio-8080-exec-8] c.mk.mapper.CommodityMapper.selectById   : ==> Parameters: 1(Integer)2020-09-20 21:09:34.754 DEBUG 4824 --- [nio-8080-exec-8] c.mk.mapper.CommodityMapper.selectById   : <==      Total: 0

整合 Redis 缓存

整合 Redis 之前,需要准备 Redis 服务器,参考:

接着,在 pom.xml 文件引入以下依赖:

org.springframework.boot
spring-boot-starter-data-redis
org.apache.commons
commons-pool2

修改 application.yml 配置文件,添加 Redis 配置:

spring:  redis:    database: 0 # Database index used by the connection factory.    host: 192.168.88.158 # Redis server host.    port: 6379 # Redis server port.    password: 123456 # Login password of the redis server.    timeout: 3000 # Connection timeout.    lettuce:      pool:        max-active: 8        max-wait: 1        min-idle: 0        max-idle: 8debug: true

Spring Boot 默认使用 SimpleCacheConfiguration 配置,引入 Redis 依赖之后,Spring Boot 将使用 Redis 做为缓存,控制台将输出如下信息:

RedisCacheConfiguration matched:      - @ConditionalOnClass found required class 'org.springframework.data.redis.connection.RedisConnectionFactory' (OnClassCondition)      - Cache org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration automatic cache type (CacheCondition)      - @ConditionalOnBean (types: org.springframework.data.redis.connection.RedisConnectionFactory; SearchStrategy: all) found bean 'redisConnectionFactory'; @ConditionalOnMissingBean (types: org.springframework.cache.CacheManager; SearchStrategy: all) did not find any beans (OnBeanCondition)
上一篇:CSS - 绘制箭头
下一篇:Spring Boot - Redis 连通性测试

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2025年04月02日 22时22分00秒