Spring Boot | Spring Boot 默认 “缓存管理“ 、Spring Boot “缓存注解“ 介绍

目录:

    • 一、Spring Boot 默认 "缓存" 管理 :
      • 1.1 基础环境搭建
        • ① 准备数据
        • ② 创建项目
        • ③ 编写 "数据库表" 对应的 "实体类"
        • ④ 编写 "操作数据库" 的 Repository接口文件
        • ⑤ 编写 "业务操作列" Service文件
        • ⑥ 编写 "application.properties配置文件"
        • ⑦ 项目测试 ( 实际开发中的"问题突显",用 "缓存技术" 能解决这个问题 )
      • 1.2 Spring Boot "默认缓存体验"
        • (1) 使用 "@EnableCaching" 注解 开始 “缓存管理”
        • (2) 使用 "@Cacheable( )" 注解对 "数据操作方法" 进行 “缓存管理” ( 将该注解放在service类的“操作方法”上,让其对"查询结果" 进行 “缓存” )
        • (3) Spring Boot默认缓存测试
    • 二、Spring Boot "缓存注解" 介绍 :
      • 2.1 @EnableCaching 注解 ( 用在 "主程序启动类" 上,用于开启SpringBoot的 "缓存管理支持")
      • 2.2 @Cacheable( ) 注解 ( 作用于"类" 或 "方法",通常用在 "数据查询" 功能的 "方法" 上 , 将 "查询结果" 存进 "缓存空间" 中 )
      • 2.3 @CachePut 注解 ( 作用于"类" 或 "方法",通常用在 "数据更新" 功能的 "方法" 上 ,在 "更新数据库" 后 "更新缓存" )
      • 2.4 @CacheEvict 注解 ( 作用于"类" 或 "方法",通常用在 "数据删除" 方法上 ,在”删除数据库中 数据“ 后,进行删除 “缓存数据” )
      • 2.5 @Caching注解 ( 作用于"类" 或 "方法" )
      • 2.6 @CacheConfig注解 ( 作用于"类" , 为该类下 的 "缓存注解" 设置 “公共属性” )

作者简介 :一只大皮卡丘,计算机专业学生,正在努力学习、努力敲代码中! 让我们一起继续努力学习!

该文章参考学习教材为:
《Spring Boot企业级开发教程》 黑马程序员 / 编著
文章以课本知识点 + 代码为主线,结合自己看书学习过程中的理解和感悟 ,最终成就了该文章

文章用于本人学习使用 , 同时希望能帮助大家。
欢迎大家点赞👍 收藏⭐ 关注💖哦!!!

(侵权可联系我,进行删除,如果雷同,纯属巧合)


  • 缓存分布式系统中的重要组件,主要 解决数据厍数据高并发访问问题。在实际开发中,尤其是用户访问量较大网站,为了提高服务器访问性能减少数据库的压力提高用户体验,使用 缓存显得尤为重要Spring Boot缓存提供了良好的支持

一、Spring Boot 默认 “缓存” 管理 :

  • Spring框架支持透明地应用程序添加缓存并对缓存进行管理,其管理缓存核心是将缓存 应用于操作数据的方法中,从而 减少操作 数据的次数同时不会对程序本身造成任何干扰
  • Spring Boot继承了Spring框架缓存管理功能,通过使用 @EnableCaching注解开启基于注解缓存支持Spring Boot可以启动缓存管理自动化配置。

1.1 基础环境搭建

  • 使用缓存主要目的减少数据库数据访问压力提高用户体验,下面代码例子将结合数据库访问操作Spring Boot缓存管理讲行演示说明
① 准备数据

先创建了一个 数据库springbootdata,然后创建了两个表 t_articlet_comment ,并向表中插入数据。
其中评论表t_commenta_id 与文章表t_article主键id 相关联 ( t_article主键作为t_comment表外键)。

springbootdata.sql

② 创建项目

创建项目,引入相关依赖
在这里插入图片描述

③ 编写 “数据库表” 对应的 “实体类”
  • 编写t_comment表对应的 实体类Comment,并用 JPA相关注解配置映射关系 :

    Comment.java

    package com.myh.chapter_14.domain;import jakarta.persistence.*;//指定该实现类映射的数据库表@Entity(name = "t_commet") //设置ORM实体类, 并指定对应的表明
    public class Comment {//表示数据库表中主键对应的属性@Id@GeneratedValue(strategy= GenerationType.IDENTITY) //设置主键的生成策略 (主键自增)private Integer id;@Column(name = "content") //指定映射的表字段名private String content;@Column(name = "author")private String author;@Column(name = "a_id")private Integer aId;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public Integer getaId() {return aId;}public void setaId(Integer aId) {this.aId = aId;}@Overridepublic String toString() {return "Comment{" +"id=" + id +", content='" + content + '\'' +", author='" + author + '\'' +", aId=" + aId +'}';}
    }
    
④ 编写 “操作数据库” 的 Repository接口文件
  • 项目中创建 repository包,在该包创建一个用于操作数据库自定义Repository接口该接口 继承JpaRepository
    ( Repository接口 中为 操作数据库方法 )


    CommentRepository.java

    package com.myh.chapter_14.Repository;import com.myh.chapter_14.domain.Comment;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.Query;//Repository接口中为操作数据库的方法
    /*继承了JpaRepository接口,其中有操作数据库的curd方法,也用方法关键字的形式来操作数据库,或者使用@Query注解的方式来操作数据库*/
    public interface CommentRepository extends JpaRepository<Comment,Integer> {//根据评论id来修改评论作者author//通过updateComment()方法对应的@Query注解来操作数据库@Query("update t_commet c set c.author = ?1 where c.id = ?2") //通过该标签来操作数据库public int updateComment(String author, Integer id);
    }
    
⑤ 编写 “业务操作列” Service文件
  • 项目中创建 service包,在该包创建一个用于操作Comment相关业务操作Service实体类

    CommentController.java

    package com.myh.chapter_14.controller;import com.myh.chapter_14.domain.Comment;
    import com.myh.chapter_14.service.CommentService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestController;//@ResponseBody注解 : 将方法的返回值转换为"指定类型",存入响应体中,然后响应给“前端”
    @RestController // 该注解等于 @ResponseBody 注解 + @Controller注解
    public class CommentController {@Autowiredprivate CommentService commentService;/*** findById()方法*/@GetMapping("/get/{id}") //路径变量,用@PathVariable()注解来接受路径变量public Comment findById(@PathVariable("id") int comment_id) {Comment comment = commentService.findById(comment_id);return comment;}/*** updateComment()方法*/@GetMapping("/update/{id}/{author}") //路径变量,用@PathVariable()注解来接受路径变量public Comment updateComment(@PathVariable("id") int comment_id,@PathVariable("author") String author) {Comment comment = commentService.findById(comment_id);comment.setAuthor("张三");Comment updateComment = commentService.updateComment(comment);return updateComment;}/*** deleteComment()方法*/@GetMapping("/delete/{id}}") //路径变量,用@PathVariable()注解来接受路径变量public void deleteComment(@PathVariable("id") int comment_id) {commentService.deleteComment(comment_id);}}
    
⑥ 编写 “application.properties配置文件”
  • application.properties

    #配置数据库信息
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/springbootdata?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT&nullCatalogMeansCurrent=true
    spring.datasource.username=root
    spring.datasource.password=root#显示使用JPA进行"数据库查询"的SQL语句,用于展示操作的Sql语句
    #这个属性决定了是否应该在控制台打印出SQL查询语句
    spring.jpa.show-sql=true
    
⑦ 项目测试 ( 实际开发中的"问题突显",用 “缓存技术” 能解决这个问题 )
  • 启动项目项目启动成功后,在浏览器上访问 http://localhost:8080/get/1 查询 id为1用户评论信息,此时 假设在浏览器一直刷新访问这个 网址,就会有以下 这种情况:

    浏览器每刷新访问一次服务器端控制台就会输出一条sql语句同时会再执行一次sql操作页面显示还是那一条数据 。( 存在一个实际开发中的问题,这个问题会消耗数据库的性能消耗服务器资源,同时数据库性能下降也会影响 用户的体验 ,这是一个要被解决的问题 )


    在这里插入图片描述

    在这里插入图片描述

  • 之所以出现上面两图情况,这是因为 没有Spring Boot项目开启缓存管理。在没有缓存管理情况下,虽然数据表中数据没有发生变化,但是 每执行 一次查询操作本质执行同样的SQL 语句),都会访问一次数据库并执行一次SQL 语句
    随着时间的积累,系统的用户不断增加数据规模越来越大数据库的操作会直接影响用户的使用体验,此时使用缓存往往是解决这一问题非常好的一种手段

1.2 Spring Boot “默认缓存体验”

  • 前面搭建Web应用 基础上,开启Spring Boot默认支持缓存体验Spring Boot缓存使用效果
(1) 使用 “@EnableCaching” 注解 开始 “缓存管理”
  • 使用 @EnableCaching注解开启 基于 注解缓存支持 ,在项目启动类加入该注解即可 :

    @SpringBootApplication
    @EnableCaching //开启SpringBoot基于注解的"缓存管理"支持
    public class Chapter14Application {public static void main(String[] args) {SpringApplication.run(Chapter14Application.class, args);}
    }
    
(2) 使用 “@Cacheable( )” 注解对 “数据操作方法” 进行 “缓存管理” ( 将该注解放在service类的“操作方法”上,让其对"查询结果" 进行 “缓存” )
  • 使用 “@Cacheable ( )注解对 “数据操作方法” 进行 “缓存管理”。 将 @Cacheable( )注解标注Service类查询方法 上,对查询结果进行缓存 :


    在这里插入图片描述


CommentService.java :

package com.myh.chapter_14.service;import com.myh.chapter_14.Repository.CommentRepository;
import com.myh.chapter_14.domain.Comment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;import java.util.Optional;/*** 使用 @Cacheable()注解开启“缓存管理” : 将查询到的结果存储到“缓存空间”中,下次访问该方法时,不会再去查数据库,而是从“缓存空间”中拿数据*/
@Service //将该类加入到ioc容器中
public class CommentService { //业务操作类 : 关于操作Comment类相关的业务的CommentService业务类 ( 该类执行业务操作代码 )@Autowiredprivate CommentRepository commentRepository;/***  调用CrudRepository接口中的findById()方法来操作数据库*//*①@Cacheable()注解的作用 :添加该注解后,Spring在执行该方法之前会检查缓存中是否有“该查询”的结果,如果有则从其中取出结果,如果没有才去数据库查询②@Cacheable("comment") 中的comment为该"缓存空间"的名称,用区分不同的缓存key = "comment_id" 设置该缓存数据的key(“缓存数据”的唯一标识),key属性的值默认为: 方法中参数的值key : 表示"缓存数据"的唯一标识*///@Cacheable(cacheNames = "comment",key = "comment_id") //开启“缓存管理”,缓存空间名称为: comment
public Comment findById(int comment_id) { //comment_id的值默认为 该“缓存”的唯一标识( "缓存数据"对应的“key”)//调用CrudRepository接口中的findById()方法来操作数据库,有一个返回值类型为Optional<T>类型的对象Optional<Comment> optional = commentRepository.findById(comment_id);if (optional.isPresent()) { //用于检查Optional对象"是否包含"一个"值"return  optional.get(); //返回这个Optional对象包含的"值"}return null;}}

上述代码中,在CommentService类中的 findByld( int comment_id )方法上添加了 @Cacheable( )注解,该注解作用查询结果 : Comment 存放
Spring Boot默认缓存 中 名称为 : comment名称空间 中。

对应缓存唯一标识 ( 即缓存数据对应的主键 key ) 默认方法参数comment_id的值

(3) Spring Boot默认缓存测试
  • 启动项目,通过浏览器继续访问 “http:/localhost:8080/get/1” 查询id1用户评论信息。此时不论浏览器刷新多少次访问同一个用户评论信息页面的查询结果会显示同一条数据,但**重点**是 后端不用进行多次数据库操作了,此时后端只进行了一次数据库操作


    在这里插入图片描述


    在这里插入图片描述

二、Spring Boot “缓存注解” 介绍 :

  • 上面的内容我们通过使用 @EnableCaching@Cacheable 注解实现了 Spring Boot默认基于注解缓存管理,除此之外,还有更多的 缓存注解以及注解属性可以配置优化缓存管理

2.1 @EnableCaching 注解 ( 用在 “主程序启动类” 上,用于开启SpringBoot的 “缓存管理支持”)

  • @EnableCaching 注解 是由 Spring框架提供的,Spring Boot框架该注解进行了 继承,该注解需要 配置在类上(在Spring Boot中 ,通常 配置项目启动类上),用于 开启基于注解缓存支持

  • @EnableCaching 注解 代码例子如

    Chapter14Application.java ( 主程序启动类 ) :

    @SpringBootApplication
    @EnableCaching //开启SpringBoot的"缓存管理支持"
    public class Chapter14Application {public static void main(String[] args) {SpringApplication.run(Chapter14Application.class, args);}
    }
    

2.2 @Cacheable( ) 注解 ( 作用于"类" 或 “方法”,通常用在 “数据查询” 功能的 “方法” 上 , 将 “查询结果” 存进 “缓存空间” 中 )

  • @Cacheable 注解也是由 Spring 框架提供的,可以作用于类或方法通常用数据查询查法 上),用于 方法的查询结果 进行缓存存储 ( 将查询结果 存储在 “缓存空间” 中 )。

    @Cacheable 注解注解一般用在 select 功能方法上 ,当调用该方法时,先根据key去“缓存空间”中查询是否有“缓存数据”,如果,自然就用该“缓存数据” , 如果没有,则去数据库查询数据,然后将该数据 存储到 “缓存空间”中,作为“ 缓存数据”)

  • @Cacheable( )注解执行顺序是,先进行"缓存查询" ( 即在缓存空间查询是否有符合要求数据 ) ,如果 为空进行 “方法查询”如果不为空,则直接使用缓存数据 ( 此时不再进行"方法查询" )

  • @Cacheable( )注解多个属性,用于对“缓存存储”进行相关配置具体属性 及 说明 如下表所示

    属性名说明
    value / cacheNames指定缓存空间名称必配属性。这两个属性 二选一使用
    ps :
    如果 @Cacheable( ) 注解 只有 valuecacheNames( )一个属性时,则该属性的属性名可省略
    key指定 缓存数据key ( 该 “ 缓存”的 唯一标识),默认使用 方法参数值,可以使用SpEL表达式
    keyGenerator指定 缓存数据key生成器与key属性二选一使用
    cacheManager指定 缓存管理器
    cacheResolver指定 缓存解析器,与 cacheManager 属性二选一 使用。
    condition指定符合某条件“进行” 数据缓存
    unless指定符合某条件下“不进行” 数据缓存
    sync指定 是否使用 “异步缓存”默认 false
  • @Cacheable( ) 注解 代码例子如 :

    CommentService.java :

     /*** 使用 @Cacheable()注解开启“缓存管理” : 将查询到的结果存储到“缓存空间”中,下次访问该方法时,不会再去查数据库,而是从“缓存空间”中拿数据*/
    @Service //将该类加入到ioc容器中
    public class CommentService { //业务操作类 : 关于操作Comment类相关的业务的CommentService业务类 ( 该类执行业务操作代码 )@Autowiredprivate CommentRepository commentRepository;/*@Cacheable()注解的作用 :添加该注解后,Spring在执行该方法之前会检查缓存中是否有“该查询”的结果,如果有则从其中取出结果,如果没有才去数据库查询@Cacheable("comment") 中的comment为该"缓存空间"的名称,用区分不同的缓存
    */@Cacheable(cacheNames = "comment") //开启“缓存管理”,该"缓存空间"名称为: comment/*如果没有显式地指定该"缓存空间"的key,那么该方法的"参数的值"则为该"key的值",所以此处有 comment_id这个参数的值默认为 该“缓存”的唯一标识( "缓存数据"对应的“key”) ,value则是该"缓存值"*/public Comment findById(int comment_id) {//调用CrudRepository接口中的findById()方法来操作数据库,有一个返回值类型为Optional<T>类型的对象Optional<Comment> optional = commentRepository.findById(comment_id);if (optional.isPresent()) { //用于检查Optional对象"是否包含"一个"值"return  optional.get(); //返回这个Optional对象包含的"值"}return null;}}
    

value / cacheNames 属性 ( 指定存储"缓存数据"的 “空间的名称” )

  • value属性cacheNames 属性 作用相同,用于 指定缓存名称空间 ( 指定“缓存空间”的 名称 ),可以同时指定多个名称空间(例如 @Cacheable ( cacheNames = {“comment1”,“comment2”} ) )。

  • 如果 @Cacheable( ) 注解 只有 valuecacheNames( )一个属性时,则该属性的属性名可省略,例如 @Cacheable(“comment”)指定了缓存的名称空间为 comment

  • @Cacheable( ) 注解的 value / cacheNames 属性代码例子 如 :

    CommentService.java

     /*** 使用 @Cacheable()注解开启“缓存管理” : 将查询到的结果存储到“缓存空间”中,下次访问该方法时,不会再去查数据库,而是从“缓存空间”中拿数据*/
    @Service //将该类加入到ioc容器中
    public class CommentService {  @Autowiredprivate CommentRepository commentRepository;/*@Cacheable()注解的作用 :添加该注解后,Spring在执行该方法之前会检查缓存中是否有“该查询”的结果,如果有则从其中取出结果,如果没有才去数据库查询@Cacheable("comment") 中的comment为该"缓存空间"的名称,用区分不同的缓存*/@Cacheable(cacheNames = "comment") //开启“缓存管理”,该"缓存空间"名称为: commentpublic Comment findById(int comment_id) {//调用CrudRepository接口中的findById()方法来操作数据库,有一个返回值类型为Optional<T>类型的对象Optional<Comment> optional = commentRepository.findById(comment_id);if (optional.isPresent()) { //用于检查Optional对象"是否包含"一个"值"return  optional.get(); //返回这个Optional对象包含的"值"}return null;}//该方法的"查询结果"会被缓存在 comment 和 commentCache 这两个"缓存空间"中@Cacheable(cacheNames = {"comment", "commentCache"})public Comment findById2(int comment_id) {Optional<Comment> optional = commentRepository.findById(comment_id);if (optional.isPresent()) {return  optional.get();}return null;}}
    

key 属性 ( 指定“缓存数据” 对应的 “key / 唯一标识” , 通过这个key就能找到该“缓存数据” ,key属于 和 keyGenerator属性 “二选一” 使用 )

  • key属性作用 是指定缓存数据 对应的 唯一标识 默认使用注解标记方法 中的 “参数” ( 默认使用方法中的参数作为key ), 也可以使用 SpEL表达式

  • 缓存数据本质Map类型数据key 用于 指定唯一标识value用于指定 缓存数据
    如果缓存数据时,没有指定key属性Spring boot默认提供的
    配置类 SimpleKeyGenerator会通过 generateKey( Object…params)方法参数生成key值默认情况下,如果 generateKey( )方法有一个参数参数值就是key属性的值

    如果generateKey( )方法没有参数,那么key属性是一个空参SimpleKey[]对象,如果有多个参数,那么 key属性是一个带参SimpleKey[params1],[param2,…]]对象

  • 除了 使用默认key属性值外,还可以手动指定key属性值,或者是使用Spring框架提供的 SpEL表达式。关于**缓存中支持
    SpEL表达式说明
    下表所示** :

    名称位置描述
    methodNameroot对象当前被调用方法名#root.methodName
    methodroot对象当前被调用方法#root.method.name
    targetroot对象当前被调用的 目标对象实例#root.target
    targetClassroot对象当前被调用的目标对象#root.targetClass
    argsroot 对象当前 被调用的方法参数列表#root.args[0]
    cachesroot对象当前 被调用的方法缓存列表#root.caches[0].name
    Argument执行上下文当前 被调用方法参数,可以用#参数名或者#a0、#p0的形式表示
    ( 0代表参数索引,从0开始 )
    #comment_id、#a0、#p0
    result执行上下文当前方法执行后返回结果#result
  • @Cacheable( ) 注解的 key 属性代码例子 如 :

    CommentService.java

      /*** 使用 @Cacheable()注解开启“缓存管理” : 将查询到的结果存储到“缓存空间”中,下次访问该方法时,不会再去查数据库,而是从“缓存空间”中拿数据*/
    @Service //将该类加入到ioc容器中
    public class CommentService2 { //业务操作类@Autowiredprivate CommentRepository commentRepository;/*如果没有显式地指定该"缓存空间"的key,那么该方法的"参数的值"则为该key的值,所以此处有 comment_id这个参数的值默认为 该“缓存”的唯一标识( "缓存数据"对应的“key”)显式地指定该"缓存空间"的key, key为方法中参数的"值",此时value就是该"缓存空间"中的"缓存值"*//***  1.使用"方法参数" 作为 key**  #comment_id 是一个 "SpEL表达式" , 配置该key,意味着每次调用该方法时,都会以key="comment_id的值",来在"缓存空间"中查找 "缓存值"*/@Cacheable(cacheNames = "comment",key = "#comment_id") //显式地指定该"缓存空间"的key为comment_id这个参数的"值"public Comment findById(int comment_id) {//调用CrudRepository接口中的findById()方法来操作数据库,有一个返回值类型为Optional<T>类型的对象Optional<Comment> optional = commentRepository.findById(comment_id);if (optional.isPresent()) { //用于检查Optional对象"是否包含"一个"值"return  optional.get(); //返回这个Optional对象包含的"值"}return null;}/***  2.使用 多个 "方法参数" 作为 key** "#comment_id + '-' +#userName" :* 是一个 "SpEL表达式" , 配置该key,意味着每次调用该方法时,都会以key="#comment_id + '-' +#userName"的值,来在"缓存空间"中查找 "缓存值"*/@Cacheable(cacheNames = "comment",key = "#comment_id + '-' +#userName")public Comment findById2(int comment_id,String userName) {//调用CrudRepository接口中的findById()方法来操作数据库,有一个返回值类型为Optional<T>类型的对象Optional<Comment> optional = commentRepository.findById(comment_id);if (optional.isPresent()) { //用于检查Optional对象"是否包含"一个"值"return  optional.get(); //返回这个Optional对象包含的"值"}return null;}/***  3.使用 "方法参数" 集合 "对象的值" 作为 key** "#comment_id + '-' +#user.id" :* 是一个 "SpEL表达式" , 配置该key,意味着每次调用该方法时,都会以key="#comment_id + '-' +#user.id"的值,来在"缓存空间"中查找 "缓存值"*/@Cacheable(cacheNames = "comment",key = "#comment_id + '-' +#user.id")public Comment findById3(int comment_id, User user) {//调用CrudRepository接口中的findById()方法来操作数据库,有一个返回值类型为Optional<T>类型的对象Optional<Comment> optional = commentRepository.findById(comment_id);if (optional.isPresent()) { //用于检查Optional对象"是否包含"一个"值"return  optional.get(); //返回这个Optional对象包含的"值"}return null;}}
    

CommentController.java

  //@ResponseBody注解 : 将方法的返回值转换为"指定类型",存入响应体中,然后响应给“前端”@RestController // 该注解等于 @ResponseBody 注解 + @Controller注解public class CommentController {@Autowiredprivate CommentService commentService;@GetMapping("/get/{id}") //路径变量,用@PathVariable()注解来接受路径变量public Comment findById(@PathVariable("id") int comment_id) {Comment comment = commentService.findById(comment_id);return comment;}@GetMapping("/get/{id}/{userName}") public Comment findById2(@PathVariable("id") int comment_id,@PathVariable("userName") String userName) {Comment comment = commentService.findById2(comment_id,userName);return comment;}@GetMapping("/get3/{id}") public Comment findById3(@PathVariable("id") int comment_id, User user) {Comment comment = commentService.findById3(comment_id,user);return comment;}}

keyGenerator属性 ( keyGenerator属性 : 指定生成keykey值生成器",该属性key属性二选一使用 )

  • keyGenerator属性kev属性 本质作用 相同,都是用于指定缓存数据key,只不过 keyGenerator 属性指定的不是具休的 key值,而是 key值生成器规则其中 指定的生成器 生成 具体的key 。使用时, kevGenerator属性与key属性要 二者选一。关于自定义key值生成器定义,可以参考Sprina Boot默认配置类SimoleKavGenerator的定义方式。

  • @Cacheable( ) 注解keyGenerator属性代码例子如

    CommentService.java :

      /*** 使用 @Cacheable()注解开启“缓存管理” : 将查询到的结果存储到“缓存空间”中,下次访问该方法时,不会再去查数据库,而是从“缓存空间”中拿数据*/
    @Service //将该类加入到ioc容器中
    public class CommentService { //业务操作类@Autowiredprivate CommentKeyGenerator commentKeyGenerator;/***  通过实现KeyGenerator接口,在CommentKeyGenerator中"自定义"生成key的值*  ( 现KeyGenerator接口是属于 keyGenerator属性的内容,该属于与 key属性 "二选一" 使用)*  (KeyGenerator属性 和 key属性 "二选一" 使用 )*/@Cacheable(cacheNames = "comment",keyGenerator ="commentKeyGenerator" )  //commentKeyGenerator 为 自定义的 "keyGenerator"public Comment findById4(int comment_id,String userName) {//调用CrudRepository接口中的findById()方法来操作数据库,有一个返回值类型为Optional<T>类型的对象Optional<Comment> optional = commentRepository.findById(comment_id);if (optional.isPresent()) { //用于检查Optional对象"是否包含"一个"值"return  optional.get(); //返回这个Optional对象包含的"值"}return null;}}
    

CommentKeyGenerator.java

/*** 在该类中配置key的生成规则*/
@Component //加入到IOC容器中
public class CommentKeyGenerator implements KeyGenerator {@Overridepublic Object generate(Object target, Method method, Object... params) {int commentId = (int) params[0];String userName = (String) params[1];return commentId + "_" + userName;}
}

cacheManager / cacheResolver属性

  • cacheManager ( 缓存管理器 )和 cacheResolver ( 缓存解析器 )属性分别用于指定 缓存管理器缓存解析器,这两个属性也是

    二选一使用,默认情况不需要配置,如果存在多个cacheManager : 缓存管理器(如 RedisEhcache 等 ),可以 使用这两个属性分别指定

condition 属性 ( 指定条件true时,才对查询结果” 进行 缓存 )

  • condition属性用于对数据进行 有条件的选择性存储,只有当 指定条件true时才会对查询结果进行缓存,可以使用 SpEL表达式指定属性值

    例如@Cacheable(cacheNames = “comment ,condition =”#comment_id>10") 表示方法参数comment_id值大于10 才会对 结果数据进行缓存

  • @Cacheable( ) 注解condition 属性代码例子如

    CommentService.java

    /*** 使用 @Cacheable()注解开启“缓存管理” : 将查询到的结果存储到“缓存空间”中,下次访问该方法时,不会再去查数据库,而是从“缓存空间”中拿数据*/
    @Service //将该类加入到ioc容器中
    public class CommentService { //业务操作类@Autowiredprivate CommentRepository commentRepository;/***  @Cacheable()注解中的 condition属性 的使用 : 当指定条件为true时,才会对"查询结果"进行"缓存"*  condition = "#comment_id > 3" : 表示当comment_id的值大于3时,才会对相应的数据进行“缓存管理”,否则不会对数据进行"缓存管理"*/@Cacheable(cacheNames = "comment",condition = "#comment_id > 3")public Comment findById5(int comment_id) {//调用CrudRepository接口中的findById()方法来操作数据库,有一个返回值类型为Optional<T>类型的对象Optional<Comment> optional = commentRepository.findById(comment_id);if (optional.isPresent()) { //用于检查Optional对象"是否包含"一个"值"return  optional.get(); //返回这个Optional对象包含的"值"}return null;}}
    

    CommentController.java

    //@ResponseBody注解 : 将方法的返回值转换为"指定类型",存入响应体中,然后响应给“前端”
    @RestController // 该注解等于 @ResponseBody 注解 + @Controller注解
    public class CommentController {@Autowiredprivate CommentService commentService;@GetMapping("/get5/{id}") //路径变量,用@PathVariable()注解来接受路径变量public Comment findById35(@PathVariable("id") int comment_id) {Comment comment = commentService.findById5(comment_id);return comment;}}
    

unless属性 ( 指定条件false时,才对查询结果” 进行 缓存 )

  • unless属性作用condition属性相反,当指定的条件true 时,方法的返回值不会被缓存 ( 即当 指定条件true时,此时 不会进行“缓存指定条件false,才进行数据的缓存 )。unless属性可以使用 SpEL表达式指定

    例如@Cacheable(cacheNames = "comment ,unless= “#result==nul”) 表示只有查询结果不为空才会对结果数据进行缓存存储

  • @Cacheable( ) 注解unless属性代码例子如

    CommentService.java

    /*** 使用 @Cacheable()注解开启“缓存管理” : 将查询到的结果存储到“缓存空间”中,下次访问该方法时,不会再去查数据库,而是从“缓存空间”中拿数据*/
    @Service //将该类加入到ioc容器中
    public class CommentService2 { //业务操作类@Autowiredprivate CommentRepository commentRepository;/***  @Cacheable()注解中的 unless 的使用 : 当指定条件为false时,才会对"查询结果"进行"缓存"*  cunless = "#comment_id == -1") : 表示当comment_id的值不等于 -1时,才会对相应的数据进行“缓存管理”,否则不会对数据进行"缓存管理"*/@Cacheable(cacheNames = "comment",unless = "#comment_id == -1") //unless : 指定条件为 false才对“查询结果”进行缓存public Comment findById6(int comment_id) {System.out.println("get6");//调用CrudRepository接口中的findById()方法来操作数据库,有一个返回值类型为Optional<T>类型的对象Optional<Comment> optional = commentRepository.findById(comment_id);if (optional.isPresent()) { //用于检查Optional对象"是否包含"一个"值"return  optional.get(); //返回这个Optional对象包含的"值"}return null;}}
    

sync属性

sync属性表示数据缓存过程是否使用异步模式默认值false

2.3 @CachePut 注解 ( 作用于"类" 或 “方法”,通常用在 “数据更新” 功能的 “方法” 上 ,在 “更新数据库” 后 “更新缓存” )

  • @CachePut注解是由Spring框型提供的,可以作用于 方法 ( 通常用在"数据更新" 方法 上),该 注解的作用 是 : "更新"缓存数据
    该注解 一般用在 update功能方法 上 ,含义为 : 更新)

    CachePut 注解执行顺序先进行"方法调用" ,然后 将"方法结果" 更新到缓存 中。

    @CachePut注解提供了多个属性,这些属性@Cacheable注解属性完全相同

  • @CachePut 注解 代码例子如

    CommentController.java

    @RestController
    public class CommentController {@Autowiredprivate CommentService commentService;/***  根据id查询数据 ( 将在service层中添加"缓存管理" )*/@GetMapping("/findCommentById/{id}")public Comment findCommentById(@PathVariable("id") Integer id) { //使用@PathVariable路径变量注解来获取“路径下的变量”Comment comment = commentService.findCommentById(id);return comment;}/*** 更新数据* 在service层中将 updateComment()方法的"返回值" 更新到"缓存空间"中, 更新"缓存数据" , 这样下次调用 findCommentById()方法的时候就能* 获得最新的"缓存空间"的"缓存数据"*/@GetMapping("/updateComment/{id}/{author}")public Comment updateComment(@PathVariable("id") Integer id,@PathVariable("author") String author) {Comment comment = commentService.updateComment(id,author);return comment;}
    }
    

    CommentService.java

    @Service //将该类加入到ioc容器中
    public class CommentService {@Autowiredprivate CommentRepository commentRepository;/*** 使用@CachePut注解来当update数据库后,用该方法的返回值来更新“缓存空间”中的"缓存数据"*/@Cacheable(cacheNames = "comment",key = "#id",condition = "#id > 0")  //id参数的值大于0,才对数据进行"缓存管理"public Comment findCommentById(Integer id) {Optional<Comment> optional = commentRepository.findById(id);//判断Optional是否为空if (optional.isPresent()) { //用于检查Optional对象"是否包含"一个"值"return  optional.get(); //返回这个Optional对象包含的"值"}return null;}/*** 使用@CachePut()注解来 更新"缓存数据" , 将 updateComment()方法的"返回值" 更新存储到 "缓存空间"中** 因为 @CachePut()注解 和 @Cacheable()注解的 “缓存空间名” 和 "key的值" 相同,所以,此处在update数据后,也会及时将“更新数据”存储进“缓存空间”中* 所以此时在调用 findCommentById()方法时,会从"缓存空间"中获得“更新后的缓存数据”*/@CachePut(cacheNames = "comment",key = "#id")public Comment updateComment(Integer id, String author) {Comment comment = commentRepository.findById(id).get();comment.setAuthor(author);//更新数据comment = commentRepository.save(comment);return comment;}}
    

    CommentRepository.java

    package com.myh.chapter_14.Repository;import com.myh.chapter_14.domain.Comment;
    import org.springframework.data.jpa.repository.JpaRepository;//Repository接口中为操作数据库的方法
    /*继承了JpaRepository接口,其中有操作数据库的curd方法,也用方法关键字的形式来操作数据库,或者使用@Query注解的方式来操作数据库*/
    //继承JpaRepository接口,从其中获得操作数据库的方法
    public interface CommentRepository extends JpaRepository<Comment,Integer> { }
    

2.4 @CacheEvict 注解 ( 作用于"类" 或 “方法”,通常用在 “数据删除” 方法上 ,在”删除数据库中 数据“ 后,进行删除 “缓存数据” )

  • @CacheEvict 注解是由Spring框架提供的,可以作用于 方法(通常用在 “数据删除” 方法上),该 注解的作用"删除" 缓存数据

    @CacheEvict 注解默认执行顺序是 : 先进行方法调用然后清除缓存。( 默认先执行方法进行数据库删除然后再清除 “缓存”)

    @CacheEvict注解提供了多个属性,这些属性@Cacheable注解属性基本相同除此之外@CacheEvic注解 额外提供了两个特殊属性 : allEntriesbeforelnvocation其说明如下 :


    (1) allEntries属性

    allEntries属性表示是否清除指定缓存空间中的所有缓存数据默认值false ( 即 默认只删除指定key对应缓存数据 )。
    例如 :
    @CacheEvict (cacheNames = “comment” , allEntries = true ) 表示 : 方法执行后删除缓存空间 comment
    所有的数据


    (2 beforelnvocation属性
    beforeInvocation属性表示 是否方法执行之前 进行 缓存清除默认值为 false ( 即 默认执行方法后 进行 缓存清除)。
    例如 : @CacheEvict ( cacheNames = “comment” , beforelnvocation true) 表示方法执行之前 进行 缓存清除

    需要注意的是 :
    如果将
    @CacheEvict 注解beforelnvocation属性
    设置为 true,会 存在一定的弊端

    例如进行数据删除方法中发生了 异常,这会导致实际数据并没有被删除,但是缓存数据被提前清除了。

  • @CacheEvict 注解 代码例子如

    CommentController.java

    @Controller
    public class CommentController {@Autowiredprivate CommentService commentService;/***  根据id查询数据 ( 将在service层中添加"缓存管理" )*/@GetMapping("/delete/{id}")public void delete(@PathVariable("id") Integer id) { //使用@PathVariable路径变量注解来获取“路径下的变量”commentService.delete(id);}
    }
    

    CommentService.java :

    @Service //将该类加入到ioc容器中
    public class CommentService {@Autowiredprivate CommentRepository commentRepository;/***  根据id来删除数据*/@CacheEvict(cacheNames = "comment",key = "#id") //删除数据库的数据后,也删除“缓存空间”中的对应的“缓存数据”public void delete(Integer id) {System.out.println("数据库的数据删除成功,后清除缓存数据");}
    }
    

    CommentRepository.java

    public interface CommentRepository extends JpaRepository<Comment,Integer> {}
    

2.5 @Caching注解 ( 作用于"类" 或 “方法” )

  • 如果 处理复杂规则数据缓存 可以使用 @Caching 注解注解 作用于 ****或者 方法

  • @Caching注解包含 cacheableputevict 三个属性,这三个属性 作用等同于 @Cacheable注解@CachePut注解@CacheEvict注解实例代码如下 :

    /**
    * 使用@Caching注解
    */
    @Caching(cacheable = {@Cacheable(cacheNames = "comment", key = "#id")},put = {@CachePut(cacheNames = "comment", key = "#result.author")}
    )
    public Comment findCommentWithAuthor(Long id) {// ... 方法实现,查找评论和作者
    }
    

2.6 @CacheConfig注解 ( 作用于"类" , 为该类下 的 “缓存注解” 设置 “公共属性” )

  • @CacheConfig注解作用于,主要用于 统筹管理类中所有使用@CacheableCacheEvict@CacheEvict 注解标注方法中的公共属性,这些公共属性包括 : cacheNameskeyGeneratorcacheManagercacheResolver示例代码如下 :

    CommentService.java

      @Service //将该类加入到ioc容器中/*** 统筹该类下的所有使用@Cacheable()注解、@CachePut()注解、@CacheEvict()注解 , 比如此处统筹其下的方法的“缓存空间”都统一设置为"comment"*/
    @CacheConfig(cacheNames = "comment")
    public class CommentService {  @Autowiredprivate CommentRepository commentRepository;@Cacheable(cacheNames = "comment",key = "comment_id") public Comment findById(int comment_id) { Optional<Comment> optional = commentRepository.findById(comment_id);if (optional.isPresent()) { return  optional.get();}return null;}
    }
    

上述代码中,CommentService 类上标注了 @CacheConfig注解,同时使用 cacheNames属性缓存空间统一设置为 comment,这样在 该类中所有方法 上使用 缓存注解时 可以省略相应cacheNames属性


需要说明的是 :如果在类上使用了 @CacheConfig 注解定义了某个属性(例如 cacheNames同时又在该类方法中使用缓存注解定义相同的属性,那么该属性值会使用“就近原则”,方法上注解中的属性值为准

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

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

相关文章

JavaCard学习笔记: CAP Component 之 Class Component

文章目录 整体结构tag和size字段signature_pool_length和signature_pooltype_descriptor结构导入类型编码导入项签名示例导入类导入数组导入远程方法 interfaces[]interface_info结构flagsinteface_countsuperinterfacesinterface_name class_info_compact classes[]结构flagsi…

基于Springboot的网上商城购物系统

基于SpringbootVue的网上商城购物系统的设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 首页 商品信息 商品资讯 后台登录页面 后台管理首页 用户管理 商品分类管…

记录一个hive中跑insert语句说没创建spark客户端的问题

【背景说明】 我目前搭建离线数仓&#xff0c;并将hive的执行引擎改成了Spark&#xff0c;在将ods层的数据装载到dim层&#xff0c;执行insert语句时报如下错误 【报错】 [42000][40000] Error while compiling statement: FAILED: SemanticException Failed to get a spark…

星链全解1

星链基本信息 星链卫星的寿命约为5年&#xff0c;最终目标是发射42000颗卫星。最初&#xff0c;每颗卫星重约260公斤&#xff0c;与1吨以上的大卫星相比属于“小卫星”。现在&#xff0c;向V2版进化的星链卫星重量近800公斤&#xff0c;约为老一代卫星的3倍。 点击“星链地图…

【Entity Framework】聊一聊EF如何使用数据库函数

【Entity Framework】聊一聊EF如何使用数据库函数 文章目录 【Entity Framework】聊一聊EF如何使用数据库函数一、数据库函数的类型二、内置函数与用户定义的函数四、聚合函数、标量函数和表值函数五、Niladic函数六、EF Core 中的数据库函数映射6.1 内置函数映射6.2 EF.Functi…

Redis入门到通关之数据结构解析-QuickList

文章目录 ☃️前提概要☃️ 配置项相关☃️简要源码☃️总结 Redis中的 QuickList 是一种特殊的数据结构&#xff0c;用于存储列表类型的数据。它的设计目的是在内存中高效地存储和操作大量的列表元素&#xff0c;尤其是当列表长度很大时。 QuickList的内部结构是一个由多个节…

ARM与单片机有啥区别?

初学者必知&#xff1a;ARM与单片机到底有啥区别&#xff1f;1、软件方面这应该是最大的区别了。引入了操作系统。为什么引入操作系统&#xff1f;有什么好处嘛&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「ARM的资料从专业入门到高级教…

【Linux驱动层】iTOP-RK3568学习之路(二):vscode中设置头文件路径-完成代码自动补全

在Ubuntu下用vscode写Linux驱动层的时候&#xff0c;需要添加头文件&#xff1a; #include<linux/module.h> #include<linux/init.h> #include<linux/kernel.h>但vscode没有智能提示&#xff0c;因此需要我们手动添加自己的头文件路径&#xff1a; topeetu…

嵌入式Linux开发实操(十七):Linux Media Infrastructure userspace API

视频和无线电流媒体设备使用的Linux内核到用户空间API,包括摄像机、模拟和数字电视接收卡、AM/FM接收卡、软件定义无线电(SDR)、流捕获和输出设备、编解码器设备和遥控器。典型的媒体设备硬件如下: 媒体基础设施API就是用于控制此类设备的,分五个部分。 第一部分V4L2 API…

AI安全之问:我们的智能助手真的安全吗?

在我们日益依赖人工智能来撰写文档、编写程序代码、甚至创作艺术作品的今天&#xff0c;我们是否曾经想过这些智能系统可能面临的被恶意操纵的风险&#xff1f; 分享几个网站 GPT-3.5研究测试&#xff1a; https://hujiaoai.cn GPT-4研究测试&#xff1a; https://higpt4.cn…

MKS 质量MFC流量控制器原理及应用课件PPT

MKS 质量MFC流量控制器原理及应用课件PPT

Git | Git基本命令

Git | Git基本操作 文章目录 Git | Git基本操作一、创建Git本地仓库1、创建Git仓库2、配置Git3、理解工作区、暂存区、版本库关系 二、添加、修改与查看添加文件查看历史提交记录 修改文件查看.git文件 三、版本回退版本回退撤销修改尚未add已add但还未commit已add并commit 删除…

安信可 ESP_01SWIFI模块的使用 (电脑通过usb转tll模块连接wifi模块进行调试)

一&#xff1a;需要用到的模块 &#xff08;1&#xff09;安信可的ESP_01wifi模块 ESP-01是深圳安信可科技基于ESP8266芯片开发的串口wifi模块&#xff0c;模组集成了透传功能&#xff0c;即买即用&#xff0c;支持串口指令集&#xff0c;用户通过串口即可实现网络访问…

List的介绍

前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&#x1…

如何在本地创建一个新的Git仓库?

文章目录 **步骤一&#xff1a;开启项目之旅****步骤二&#xff1a;启动Git引擎****步骤三&#xff1a;验证仓库初始化情况****步骤四&#xff1a;填充项目内容****步骤五&#xff1a;保存更改——初次提交****&#xff08;可选步骤六&#xff1a;关联远程仓库并推送&#xff0…

双向链表-(增删减改)

声明 单链表&#xff08;增删减改&#xff09;单链表实现通讯录项目链表的专用题型-CSDN博客https://blog.csdn.net/Jason_from_China/article/details/137722729 双链表和单链表就是异曲同工 链表的分类 这里我们主要讲解的是不带头的单向不循环链表&#xff0c;在题型解析里面…

将Python机器学习模型集成到C++ Qt客户端应用程序中|Qt调用python详解

0、前言 有几个不同的选项可以将你的Python机器学习模型集成到你的C Qt客户端应用程序中。以下是一些可能的解决方案&#xff1a; 创建API&#xff1a; 将你的机器学习模型部署为一个API服务。你可以使用像Flask这样的轻量级Web框架来创建一个简单的HTTP服务。这样&#xff0…

JAVA高阶私房菜:JVM虚拟机核心概念及参数微调实验

目录 基础快速掌握 什么是JVM虚拟机 JVM的的实现 操作系统-虚拟机-JRE-JDK的关系 生产环境部署JDK还是JRE JVM内存组成部分和堆空间分布 内存组成 堆空间内存分布 内存分布 堆空间分配 JVM堆空间垃圾回收流程及JVM参数 垃圾回收流程 JVM参数分类 JVM参数格式分类 …

浅识数据结构之时间复杂度

P. S.&#xff1a;以下代码均在VS2019环境下测试&#xff0c;不代表所有编译器均可通过。 P. S.&#xff1a;测试代码均未展示头文件stdio.h的声明&#xff0c;使用时请自行添加。 文章目录 前言一. 时间复杂度1.1 时间复杂度的概念1.2 时间复杂度如何计算1.3 时间复杂度如何表…

XSS-跨站脚本攻击 漏洞详解

一、初识XSS 1、什么是XSS XSS全称跨站脚本(Cross Site Scripting)&#xff0c;为避免与层叠样式表(Cascading Style Sheets, CSS)的缩写混淆&#xff0c;故缩写为XSS。这是一种将任意 Javascript 代码插入到其他Web用户页面里执行以达到攻击目的的漏洞。攻击者利用浏览器的动…