在项目中缓存如何优化?SpringCache接口返回值的缓存【CachePut、CacheEvict、Cacheable】

SpringCache

  • 介绍(不同的缓存技术有不同的CacheManager)
  • 注解
  • 入门程序
    • 环境准备
      • 数据库准备
      • 环境准备
      • 注入CacheManager
      • 引导类上加@EnableCaching
    • @CachePut注解(缓存方法返回值)
      • 1). 在save方法上加注解@CachePut
      • 2). 测试
    • @CacheEvict注解(清理指定缓存)
      • 1). 在 delete 方法上加注解@CacheEvict
      • 2). 测试
      • 3). 在 update 方法上加注解@CacheEvict
    • @Cacheable注解(执行方法之前先查看是否有缓存,有则返回,没有则调用方法返回值放入缓存)
      • 1). 在getById上加注解@Cacheable
      • 2). 测试
      • 3). 缓存非null值
      • 4). 在list方法上加注解@Cacheable
  • 集成Redis
    • 1). pom.xml
    • 2). application.yml
    • 3). 测试

介绍(不同的缓存技术有不同的CacheManager)

Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能,大大简化我们在业务中操作缓存的代码。

Spring Cache只是提供了一层抽象,底层可以切换不同的cache实现。具体就是通过CacheManager接口来统一不同的缓存技术。CacheManager是Spring提供的各种缓存技术抽象接口。

针对不同的缓存技术需要实现不同的CacheManager:
在这里插入图片描述

注解

在SpringCache中提供了很多缓存操作的注解,常见的是以下的几个:
在这里插入图片描述
在spring boot项目中,使用缓存技术只需在项目中导入相关缓存技术的依赖包,并在启动类上使用@EnableCaching开启缓存支持即可。

例如,使用Redis作为缓存技术,只需要导入Spring data Redis的maven坐标即可。

入门程序

接下来,我们将通过一个入门案例来演示一下SpringCache的常见用法。 上面我们提到,SpringCache可以集成不同的缓存技术,如Redis、Ehcache甚至我们可以使用Map来缓存数据, 接下来我们在演示的时候,就先通过一个Map来缓存数据,最后我们再换成Redis来缓存。

环境准备

数据库准备

环境准备

在这里插入图片描述

注入CacheManager

我们可以在UserController注入一个CacheManager,在Debug时,我们可以通过CacheManager跟踪缓存中数据的变化。
在这里插入图片描述
我们可以看到CacheManager是一个接口,默认的实现有以下几种 ;

在这里插入图片描述

而在上述的这几个实现中,默认使用的是 ConcurrentMapCacheManager。稍后我们可以通过断点的形式跟踪缓存数据的变化。

引导类上加@EnableCaching

在这里插入图片描述

@CachePut注解(缓存方法返回值)

@CachePut 说明:
​作用: 将方法返回值,放入缓存
​value: 缓存的名称, 每个缓存名称下面可以有很多key
​key: 缓存的key ----------> 支持Spring的表达式语言SPEL语法

1). 在save方法上加注解@CachePut

当前UserController的save方法是用来保存用户信息的,我们希望在该用户信息保存到数据库的同时,也往缓存中缓存一份数据,我们可以在save方法上加上注解 @CachePut,用法如下:

/**
* CachePut:将方法返回值放入缓存
* value:缓存的名称,每个缓存名称下面可以有多个key
* key:缓存的key
*/
@CachePut(value = "userCache", key = "#user.id")
@PostMapping
public User save(User user){userService.save(user);return user;
}

key的写法如下:
​ #user.id : #user指的是方法形参的名称, id指的是user的id属性 , 也就是使用user的id属性作为key ;
​ #user.name: #user指的是方法形参的名称, name指的是user的name属性 ,也就是使用user的name属性作为key ;
​ #result.id : #result代表方法返回值,该表达式 代表以返回对象的id属性作为key ;
​ #result.name : #result代表方法返回值,该表达式 代表以返回对象的name属性作为key ;

2). 测试

启动服务,通过postman请求访问UserController的方法, 然后通过断点的形式跟踪缓存数据。
在这里插入图片描述
第一次访问时,缓存中的数据是空的,因为save方法执行完毕后才会缓存数据。
在这里插入图片描述
第二次访问时,我们通过debug可以看到已经有一条数据了,就是上次保存的数据,已经缓存了,缓存的key就是用户的id。
在这里插入图片描述
注意: 上述的演示,最终的数据,实际上是缓存在ConcurrentHashMap中,那么当我们的服务器重启之后,缓存中的数据就会丢失。 我们后面使用了Redis来缓存就不存在这样的问题了。

@CacheEvict注解(清理指定缓存)

@CacheEvict 说明:
​ 作用: 清理指定缓存
​ value: 缓存的名称,每个缓存名称下面可以有多个key
​ key: 缓存的key ----------> 支持Spring的表达式语言SPEL语法

1). 在 delete 方法上加注解@CacheEvict

当我们在删除数据库user表的数据的时候,我们需要删除缓存中对应的数据,此时就可以使用@CacheEvict注解, 具体的使用方式如下:

/**
* CacheEvict:清理指定缓存
* value:缓存的名称,每个缓存名称下面可以有多个key
* key:缓存的key
*/
@CacheEvict(value = "userCache",key = "#p0")  //#p0 代表第一个参数
//@CacheEvict(value = "userCache",key = "#root.args[0]") //#root.args[0] 代表第一个参数
//@CacheEvict(value = "userCache",key = "#id") //#id 代表变量名为id的参数
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id){userService.removeById(id);
}

2). 测试

要测试缓存的删除,我们先访问save方法4次,保存4条数据到数据库的同时,也保存到缓存中,最终我们可以通过debug看到缓存中的数据信息。 然后我们在通过postman访问delete方法, 如下:
在这里插入图片描述
删除数据时,通过debug我们可以看到已经缓存的4条数据:
在这里插入图片描述
当执行完delete操作之后,我们再次保存一条数据,在保存的时候debug查看一下删除的ID值是否已经被删除。
在这里插入图片描述

3). 在 update 方法上加注解@CacheEvict

在更新数据之后,数据库的数据已经发生了变更,我们需要将缓存中对应的数据删除掉,避免出现数据库数据与缓存数据不一致的情况。

//@CacheEvict(value = "userCache",key = "#p0.id")   //第一个参数的id属性
//@CacheEvict(value = "userCache",key = "#user.id") //参数名为user参数的id属性
//@CacheEvict(value = "userCache",key = "#root.args[0].id") //第一个参数的id属性
@CacheEvict(value = "userCache",key = "#result.id")         //返回值的id属性
@PutMapping
public User update(User user){userService.updateById(user);return user;
}

加上注解之后,我们可以重启服务,然后测试方式,基本和上述相同,先缓存数据,然后再更新某一条数据,通过debug的形式查询缓存数据的情况。

@Cacheable注解(执行方法之前先查看是否有缓存,有则返回,没有则调用方法返回值放入缓存)

@Cacheable 说明:
​ 作用: 在方法执行前,spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存中
​ value: 缓存的名称,每个缓存名称下面可以有多个key
​ key: 缓存的key ----------> 支持Spring的表达式语言SPEL语法

1). 在getById上加注解@Cacheable

/**
* Cacheable:在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存中
* value:缓存的名称,每个缓存名称下面可以有多个key
* key:缓存的key
*/
@Cacheable(value = "userCache",key = "#id")
@GetMapping("/{id}")
public User getById(@PathVariable Long id){User user = userService.getById(id);return user;
}

2). 测试

我们可以重启服务,然后通过debug断点跟踪程序执行。我们发现,第一次访问,会请求我们controller的方法,查询数据库。后面再查询相同的id,就直接获取到数据库,不用再查询数据库了,就说明缓存生效了。
在这里插入图片描述
当我们在测试时,查询一个数据库不存在的id值,第一次查询缓存中没有,也会查询数据库。而第二次再查询时,会发现,不再查询数据库了,而是直接返回,那也就是说如果根据ID没有查询到数据,那么会自动缓存一个null值。 我们可以通过debug,验证一下:
在这里插入图片描述
我们能不能做到,当查询到的值不为null时,再进行缓存,如果为null,则不缓存呢? 答案是可以的。

3). 缓存非null值

在@Cacheable注解中,提供了两个属性分别为: condition, unless 。

condition : 表示满足什么条件, 再进行缓存 ;
unless : 表示满足条件则不缓存 ; 与上述的condition是反向的 ;

具体实现方式如下:

/*** Cacheable:在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存中* value:缓存的名称,每个缓存名称下面可以有多个key* key:缓存的key* condition:条件,满足条件时才缓存数据* unless:满足条件则不缓存*/
@Cacheable(value = "userCache",key = "#id", unless = "#result == null")
@GetMapping("/{id}")
public User getById(@PathVariable Long id){User user = userService.getById(id);return user;
}

注意: 此处,我们使用的时候只能够使用 unless, 因为在condition中,我们是无法获取到结果 #result的。

4). 在list方法上加注解@Cacheable

在list方法中进行查询时,有两个查询条件,如果传递了id,根据id查询; 如果传递了name, 根据name查询,那么我们缓存的key在设计的时候,就需要既包含id,又包含name。 具体的代码实现如下:

@Cacheable(value = "userCache",key = "#user.id + '_' + #user.name")
@GetMapping("/list")
public List<User> list(User user){LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(user.getId() != null,User::getId,user.getId());queryWrapper.eq(user.getName() != null,User::getName,user.getName());List<User> list = userService.list(queryWrapper);return list;
}

然后再次重启服务,进行测试。
在这里插入图片描述
第一次查询时,需要查询数据库,在后续的查询中,就直接查询了缓存,不再查询数据库了。

集成Redis

在使用上述默认的ConcurrentHashMap做缓存时,服务重启之后,之前缓存的数据就全部丢失了,操作起来并不友好。在项目中使用,我们会选择使用redis来做缓存,主要需要操作以下几步:

1). pom.xml

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2). application.yml

spring:redis:host: 192.168.200.200port: 6379password: root@123456database: 0cache:redis:time-to-live: 1800000   #设置缓存过期时间,可选

3). 测试

重新启动项目,通过postman发送根据id查询数据的请求,然后通过redis的图形化界面工具,查看redis中是否可以正常的缓存数据。

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/778928.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开源AI引擎|信息抽取与文本分类项目案例:提升12345政务投诉处理效率

一、实际案例介绍 采集员案件上报流程是城市管理和问题解决的关键环节&#xff0c;涉及对案件类别的选择、案件来源的记录、详细案件描述的填写以及现场图片的上传。这一流程要求采集员准确、详细地提供案件信息&#xff0c;以便系统能够自动解析关键数据并填写相关内容&#…

ip地址改变导致nacos无法登录的解决方法

ip地址改变导致nacos无法登录的解决方法 在做黑马的springcloud课程里的黑马商城微服务项目时&#xff0c;发现使用nacos的默认账号密码&#xff08;nacos&#xff0c;nacos&#xff09;无法登录&#xff0c;项目里也没报错信息&#xff0c;虽然猜测和ip地址改变有关&#xff0…

算法---动态规划练习-6(地下城游戏)

地下城游戏 1. 题目解析2. 讲解算法原理3. 编写代码 1. 题目解析 题目地址&#xff1a;点这里 2. 讲解算法原理 首先&#xff0c;定义一个二维数组 dp&#xff0c;其中 dp[i][j] 表示从位置 (i, j) 开始到达终点时的最低健康点数。 初始化数组 dp 的边界条件&#xff1a; 对…

机器学习作业二之KNN算法

KNN&#xff08;K- Nearest Neighbor&#xff09;法即K最邻近法&#xff0c;最初由 Cover和Hart于1968年提出&#xff0c;是一个理论上比较成熟的方法&#xff0c;也是最简单的机器学习算法之一。该方法的思路非常简单直观&#xff1a;如果一个样本在特征空间中的K个最相似&…

数字化运维实战手册:构建高效运维体系的方法与实践

一本书掌握数字化运维方法&#xff0c;构建数字化运维体系 数字化转型已经成为大势所趋&#xff0c;各行各业正朝着数字化方向转型&#xff0c;利用数字化转型方法论和前沿科学技术实现降本、提质、增效&#xff0c;从而提升竞争力。 数字化转型是一项长期工作&#xff0c;包含…

Mybatis中QueryWrapper的复杂查询SQL

最近在使用QueryWrapper编写查询语句时发现复杂的SQL不会写。在网上找了半天&#xff0c;终于得到了点启示。在此做个记录以备忘。 我要实现的SQL是这样的&#xff1a; -- 实现这个复杂查询 -- 查询设备表 select * from oa_device where ((dev_code BSD1003 and dev_status…

[flume$1]记录一个启动flume配置的错误

先总结&#xff1a;Flume配置文件后面&#xff0c;不能跟注释 报错代码&#xff1a; [ERROR - org.apache.flume.SinkRunner$PollingRunner.run(SinkRunner.java:158)] Unable to deliver event. Exception follows. org.apache.flume.EventDeliveryException: Failed to open…

Termius for Mac/Win:多协议远程管理利器,你的工作效率提升神器

在数字化飞速发展的今天&#xff0c;远程管理已成为企业运营和个人工作不可或缺的一部分。而Termius&#xff0c;作为一款多协议远程管理软件&#xff0c;正以其卓越的性能和便捷的操作&#xff0c;成为广大用户的心头好。 Termius支持多种协议&#xff0c;无论是SSH、RDP还是…

查询优化-提升子查询-UNION类型

瀚高数据库 目录 文档用途 详细信息 文档用途 剖析UNION类型子查询提升的条件和过程 详细信息 注&#xff1a;图片较大&#xff0c;可在浏览器新标签页打开。 SQL: SELECT * FROM score sc, LATERAL(SELECT * FROM student WHERE sno 1 UNION ALL SELECT * FROM student…

企业微信知识库:从了解到搭建的全流程

你是否也有这样的疑惑&#xff1a;为什么现在的企业都爱创建企业微信知识库&#xff1f;企业微信知识库到底有什么用&#xff1f;如果想要使用企业微信知识库企业应该如何创建&#xff1f;这就是我今天要探讨的问题&#xff0c;感兴趣的话一起往下看吧&#xff01; | 为什么企业…

网站业务对接DDoS高防

准备需要接入的网站域名清单&#xff0c;包含网站的源站服务器IP&#xff08;仅支持公网IP的防护&#xff09;、端口信息等。所接入的网站域名必须已完成ICP备案。如果您的网站支持HTTPS协议访问&#xff0c;您需要准备相应的证书和私钥信息&#xff0c;一般包含格式为.crt的公…

游戏赛道新机会:善用数据分析,把握游戏赛道广告变现良机 | TOPON变现干货

12月10日&#xff0c;由罗斯基联合TopOn、钛动科技共同主办的《游戏赛道新机会》主题系列沙龙在武汉举办。活动邀请了国内外多家业内知名公司的负责人到场分享&#xff0c;现场嘉宾分别从自己擅长的领域出发&#xff0c;通过数据分析&#xff0c;案例复盘等多个维度方向进行讲解…

C++超市商品管理系统

一、简要介绍 1.本项目为面向对象程序设计的大作业&#xff0c;基于Qt creator进行开发&#xff0c;Qt框架版本6.4.1&#xff0c;编译环境MINGW 11.2.0。 2.项目结构简介&#xff1a;关于系统逻辑部分的代码的头文件在head文件夹中&#xff0c;源文件在s文件夹中。与图形界面…

基于Java仓库管理系统设计与实现(源码+部署文档+论文)

博主介绍&#xff1a; ✌至今服务客户已经1000、专注于Java技术领域、项目定制、技术答疑、开发工具、毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精彩专栏 推荐订阅 &#x1f447;&#x1f3fb; 不然下次找不到 Java项目精品实…

剑指Offer题目笔记20(在数组范围内二分查找)

面试题72&#xff1a; 问题&#xff1a; ​ 输入一个非负整数&#xff0c;计算它的平方根。 解决方案&#xff1a; 使用二分查找。一个数x的平方根一定小于或等于x&#xff0c;同时&#xff0c;除了0之外的所有非负整数的平方根都大于等于1&#xff0c;故该数的平方根在1到x…

数据库中的约束纯干货——主键约束

目录 &#xff08;一&#xff09;特点&#xff1a; &#xff08;二&#xff09;添加主键约束 2.1格式&#xff1a; 2.2举例&#xff1a; 2.3建立表级约束&#xff1a; 2.4建立表后增加主键约束 &#xff08;三&#xff09;复合主键 3.1格式&#xff1a; 3.2举例&#…

每日一题 --- 快乐数[力扣][Go]

快乐数 题目&#xff1a;202. 快乐数 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终变不到…

Pocket 2荧光拍摄的几个有趣玩法

荧光是现象&#xff0c;当某种常温物质经某种波长的入射光&#xff08;通常是或&#xff09;照射&#xff0c;吸收光能后进入&#xff0c;并且立即退激发并发出出射光&#xff08;通常波长比入射光的波长&#xff0c;原先看不见的短波长紫外线&#xff0c;变成在可见光波段的可…

JAVA的NIO和BIO底层原理分析

文章目录 一、操作系统底层IO原理1. 简介2. 操作系统进行IO的流程 二、BIO底层原理1. 什么是Socket2. JDK原生编程的BIO 三、Java原生编程的NIO1. 简介2. NIO和BIO的主要区别3. Reactor模式4. NIO的三大核心组件5. NIO核心源码分析 一、操作系统底层IO原理 1. 简介 IO&#x…

font-spider压缩文字

FZLanTingHei-DB-GBK是我引入的字体包名字 1.安装依赖 建议全局安装但是要注意npm配置 我配成了_globel 真是沙雕 npm install font-spider -g2. 在static文件夹新增一个文件夹font-spider 同时在font-spider文件夹里面新增index.css和index.html index.css font-face {f…