Spring-Cache 缓存

1.简介

 2.SpringCache 整合

简化缓存开发

1.导入依赖

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

2.redis 作为缓存场景

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

3. 配置类

1.自动配置

自动配置了哪些

CacheAutoConfiguration 自动导入 RedisCacheConfiguration

自动配好了缓存管理器  RedisCacheManager

2.手动需要得yaml配置

spring:
#缓存
  cache:
    type: redis

3.测试 使用缓存

1.开启缓存
 1.使用redis作为缓存
spring:
#缓存
  cache:
    type: redis
@EnableCaching //放在主启动类上
2.使用注解就可以完成缓存
 //每一个需要缓存的数据我们都来指定要放到哪个名字的缓存 [缓存的分区(按照业务类型分区)]
    @Cacheable({"category"}) //代表这个数据是可以缓存的 当前方法的结果需要缓存,如果缓存中有,方法不用调用。
    //如果缓存中没有,调用方法,将方法结果放入缓存
    @Override
    public List<CategoryEntity> getLevel1Category() {
        System.out.println("调用了数据库getLevel1Category");
        List<CategoryEntity> parentCid = this.list(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));

        return parentCid;
    }

    //默认行为
    //1.如果缓存中有,方法不用调用
    //2.key默认自动生成 : 缓存的名字::SimpleKey [](自主生产的key的值)
    //3.缓存的value的值 默认使用JDK序列化机制 将序列化后的数据存入Redis
    //4.默认ttl时间 -1;

 

//自定义
    //1. 指定生成的缓存使用的key key 属性指定,接受一个SPEL 表达式  ${}  #{}
    //2. 指定缓存的过期时间 yml 配置文件中修改ttl
    //3. 将数据存入JSON标准格式

 

 

    public  String mycategorykey="我自定义的key";
    @Override
//    @Cacheable(value = {"category"}, key = "'level1Category'")
//    @Cacheable(value = {"category"}, key = "#root.method.name")//root是当前上下文的意思 method 是方法 可以有参数 各种东西
    @Cacheable(value = {"category"}, key = "#root.target.mycategorykey")//root 可以拿到当前对象 当前方法 当前参数
    public List<CategoryEntity> getLevel1Category() {
        System.out.println("调用了数据库getLevel1Category");
        List<CategoryEntity> parentCid = this.list(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));

        return parentCid;
    }

 2.自定义缓存设置

保存的数据为json格式

1.讲一下 缓存redis配置类的 原理 

CacheAutoConfiguration->RedisCacheConfiguration->自动配置了缓存管理器 RedisCacheManager->初始化所有的缓存->每个缓存觉得使用什么配置->如果RedisCacheConfiguration有在 容器中自己 配置,就要用自己的配置,否则就用默认的配置

所以,我们只需要给容器中放入一个RedisCacheConfiguration即可

就会应用到当前缓存管理器的所有缓存中

2.配置类
package com.jmj.gulimall.product.config;

import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
@EnableCaching
public class MyCacheConfig {


    /**
     * 给了默认配置文件就不生效了
     * 因为 条件判断了 if config !=null  就返回
     * 也不需要加@EnableConfigurationProperties(CacheProperties.class)
     * 因为默认自动装配类已经加入这个
     * @return
     */
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(
            CacheProperties cacheProperties
    ) {

        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        /**
         * public RedisCacheConfiguration entryTtl(Duration ttl) { 是new新对象 得要覆盖上
         * 		return new RedisCacheConfiguration(ttl, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
         * 				valueSerializationPair, conversionService);
         *        }
         */

        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));

        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        CacheProperties.Redis redisProperties = cacheProperties.getRedis();

        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }

        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixKeysWith(redisProperties.getKeyPrefix());
        }

        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }

        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }

        return config;


    }


}
#配置数据源
spring:
#缓存
  cache:
    redis:
      time-to-live: 36000000  #单位 ms 先设定一个小时过期时间
      use-key-prefix: false #不使用,原来的前缀就没有了 key是什么 就是什么
      key-prefix: CACHE_ #所有Key都设置一个前缀来区分  如果指定了 前缀就用指定的,没有就用默认的 name::[]
      cache-null-values: true #是否缓存入空值 可以解决缓存穿透
    type: redis
3.实现 
  //每一个需要缓存的数据我们都来指定要放到哪个名字的缓存 [缓存的分区(按照业务类型分区)]
    //代表这个数据是可以缓存的 当前方法的结果需要缓存,如果缓存中有,方法不用调用。
    //如果缓存中没有,调用方法,将方法结果放入缓存
    //默认行为
    //1.如果缓存中有,方法不用调用
    //2.key默认自动生成 : 缓存的名字::SimpleKey [](自主生产的key的值)
    //3.缓存的value的值 默认使用JDK序列化机制 将序列化后的数据存入Redis
    //4.默认ttl时间 -1;

    //自定义
    //1. 指定生成的缓存使用的key key 属性指定,接受一个SPEL 表达式   #{}
    //2. 指定缓存的过期时间 yml 配置文件中修改ttl
    //3. 将数据存入JSON标准格式

    public  String mycategorykey="我自定义的key";
    @Override
//    @Cacheable(value = {"category"}, key = "'level1Category'")
//    @Cacheable(value = {"category"}, key = "#root.method.name")//root是当前上下文的意思 method 是方法 可以有参数 各种东西
    @Cacheable(value = {"category"}, key = "#root.target.mycategorykey")//root 可以拿到当前对象 当前方法 当前参数
    public List<CategoryEntity> getLevel1Category() {
        System.out.println("调用了数据库getLevel1Category");//线程不安全,需要加分布式锁和读写锁
        List<CategoryEntity> parentCid = this.list(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));

        return parentCid;
    }
1.删除缓存
    /**
     * 级联更新所有关联数据
     * @param category
     * @throws Exception
     */
    @CacheEvict(value = "category", key = "'category::tree'")//删除缓存
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDetails(List<CategoryEntity> category) throws Exception {
        category.stream().filter(c -> StringUtils.isNotBlank(c.getName())).forEach(c -> {


            List<CategoryBrandRelationEntity> updateList = categoryBrandRelationService
                    .list(new QueryWrapper<CategoryBrandRelationEntity>().eq("catelog_id", c.getCatId()))
                    .stream().peek(r -> r.setCatelogName(c.getName())).collect(Collectors.toList());
            categoryBrandRelationService.updateBatchById(updateList);
        });
        this.updateBatchById(category);

    }
2.存入 
  @Override
    @Cacheable(value = {"category"}, key = "'category::tree'")
    public List<CategoryEntity> listWithTree() {
        //1.查出所有分类
        System.out.println("三级分类查询数据库");
        List<CategoryEntity> all = this.list();
        //2.组装成父子的属性结构
        List<CategoryEntity> level1Menus = all
                .stream()
                .filter(c -> c.getParentCid().equals(0L))
                .map(categoryEntity -> categoryEntity.setChildren(getChildrenCategory(all, categoryEntity.getCatId())))
                //大于放后面 升序
                .sorted(Comparator.comparing(CategoryEntity::getSort))
                .collect(Collectors.toList());

        return level1Menus;
    }
3.要操作多个 
//    @CacheEvict(value = "category", key = "'category::tree'")//删除缓存
    //如果多操作的话
    @Caching(evict = {
            @CacheEvict(value = "category", key = "'category::tree'"),
            @CacheEvict(value = "category", key = "#root.target.mycategorykey")
    })
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDetails(List<CategoryEntity> category) throws Exception {
        category.stream().filter(c -> StringUtils.isNotBlank(c.getName())).forEach(c -> {


            List<CategoryBrandRelationEntity> updateList = categoryBrandRelationService
                    .list(new QueryWrapper<CategoryBrandRelationEntity>().eq("catelog_id", c.getCatId()))
                    .stream().peek(r -> r.setCatelogName(c.getName())).collect(Collectors.toList());
            categoryBrandRelationService.updateBatchById(updateList);
        });
        this.updateBatchById(category);

    }
4.删除这个分区下所有数据 (失效模式)

存储同一类型的数据,都可以指定一个分区

    @CacheEvict(value = "category", allEntries = true)// 设定了 use-key-prefix: false 这个如果没有这个分类,将全部缓存删除
use-key-prefix必须要为true  和  key-prefix: 不设置 这个才有用
5.双写模式

@CachePut//双写模式

3.SpringCache的不足

基本都能解决,唯独缓存击穿特别一点

 这样就是加了个本地锁,本地也就是放一个过来

  @Override
    @Cacheable(value = {"category"}, key = "'tree'",sync = true)
    public List<CategoryEntity> listWithTree() {
        //1.查出所有分类
        System.out.println("三级分类查询数据库");
        List<CategoryEntity> all = this.list();
        //2.组装成父子的属性结构
        List<CategoryEntity> level1Menus = all
                .stream()
                .filter(c -> c.getParentCid().equals(0L))
                .map(categoryEntity -> categoryEntity.setChildren(getChildrenCategory(all, categoryEntity.getCatId())))
                //大于放后面 升序
                .sorted(Comparator.comparing(CategoryEntity::getSort))
                .collect(Collectors.toList());

        return level1Menus;
    }

只有cacheable 查询注解的时候 才能够加锁

虽然加的是本地锁,但是一台服务器只能一个访问,也是够用了

4.总结

常规数据(读多写少,即时性,一致性要求不高的数据 ):完全可以使用springcache(写模式:只有数据有过期时间 就完全足够了 这样可以保证数据的最终一致性)

特殊数据 (特殊设计)例如 canal 感知数据库去更新 缓存

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

c#与欧姆龙PLC通信——如何更改PLC的IP地址

前言 我们有时候需要改变欧姆龙Plc的ip地址,下图有两种更改方式,一种是已知之前Plc设置的Ip地址,还有一种是之前不知道Pl的Ip地址是多少,下面分别做介绍。 1、已知PLC的IP地址的情况下更改地址 假设已知PLC的Ip地址,比如本文中PLC的IP为192.168.1.2,我首先将电脑的IP地…

宝塔面板以www用户运行composer

方式一 执行命令时指定www用户 sudo -u www composer update方式二 在网站配置中的composer选项卡中选择配置运行

ROS2从入门到精通5-1:详解代价地图与costmap插件编写(以距离场ESDF为例)

目录 0 专栏介绍1 代价地图介绍1.1 基本概念1.2 代价定义 2 代价地图配置2.1 通用配置2.2 障碍层配置2.3 静态层配置2.4 膨胀层配置 3 代价地图插件案例3.1 构造地图插件类3.2 注册并导出插件3.3 编译与使用插件 0 专栏介绍 本专栏旨在通过对ROS2的系统学习&#xff0c;掌握RO…

【格密码基础】旋转格的性质

目录 一. 回顾ZSVP问题 二. 基于ZSVP问题的密码系统 三. 格基旋转与Gram矩阵 四. 补充矩阵QR分解 4.1 矩阵分解 4.2 举例 前序文章请参考&#xff1a; 【格密码基础】详解ZSVP问题-CSDN博客 一. 回顾ZSVP问题 根据之前的讨论我们知道解决ZSVP问题的计算复杂度为&#x…

链路追踪系列-01.mac m1 安装zipkin

下载地址&#xff1a;https://hub.docker.com/r/openzipkin/zipkin jelexjelexxudeMacBook-Pro zipkin-server % pwd /Users/jelex/Documents/work/zipkin-server 先启动Es: 可能需要先删除 /Users/jelex/dockerV/es/plugins 目录下的.DS_Store 当端口占用时再次启动&#x…

PostgreSQL 中如何处理数据的并发读写和锁等待超时?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 PostgreSQL 中如何处理数据的并发读写和锁等待超时一、并发读写的基本概念&#xff08;一&#xff09;…

【日常记录】【插件】excel.js 的使用

文章目录 1. 引言2. excel.js2.1 创建工作簿和工作表2.2 excel文件的导出2.3 excel文件的导入2.4 列2.5 行2.6 添加行2.7 单元格2.8 给总价列设置自动计算(除表头行) 3. 总结参考链接 1. 引言 前端导出excel文件常用库一般是 excel.js 和 xlsx.js xlsx.js 导出数据确实方便&…

技术成神之路:设计模式(六)策略模式

1.介绍 策略模式&#xff08;Strategy Pattern&#xff09;是一种行为型设计模式&#xff0c;它定义了一系列算法&#xff0c;封装每一个算法&#xff0c;并使它们可以相互替换。策略模式使得算法的变化独立于使用算法的客户端。 2.主要作用 策略模式的主要作用是将算法或行为…

大数据基础:Hadoop之Yarn重点架构原理

文章目录 Hadoop之Yarn重点架构原理 一、Yarn介绍 二、Yarn架构 三、Yarn任务运行流程 四、Yarn三种资源调度器特点及使用场景 Hadoop之Yarn重点架构原理 一、Yarn介绍 Apache Hadoop Yarn(Yet Another Reasource Negotiator&#xff0c;另一种资源协调者)是Hadoop2.x版…

优化理论——迭代方法

线性回归建模 训练&#xff0c;预测 { ( x ( i ) , y ( i ) ) } \{(x^{(i)},y^{(i)})\} {(x(i),y(i))} ⼀个训练样本&#xff0c; { ( x ( i ) , y ( i ) ) ; i 1 , ⋯ , N } \{(x^{(i)},y^{(i)});i1,\cdots ,N\} {(x(i),y(i));i1,⋯,N} 训练样本集 { ( x 1 ( i ) , x 2 ( i…

爬虫管理解决方案:让数据收集变得高效且合规

一、为何数据收集的效率与合规性同等重要&#xff1f; 随着大数据技术的飞速发展&#xff0c;数据收集已成为企业决策与市场洞察的核心驱动力。然而&#xff0c;在信息海洋中精准捕捞的同时&#xff0c;如何确保这一过程既高效又不触碰法律的红线&#xff0c;是每个数据实践者…

vue实现动态图片(gif)

目录 1. 背景 2. 分析 3. 代码实现 1. 背景 最近在项目中发现一个有意思的小需求&#xff0c;鼠标移入一个盒子里&#xff0c;然后盒子里的图就开始动起来&#xff0c;就像一个gif一样&#xff0c;然后鼠标移出&#xff0c;再按照原来的变化变回去&#xff0c;就像变形金刚…

YOLOv5和LPRNet的车牌识别系统

车牌识别系统 YOLOv5和LPRNet的车牌识别系统结合了深度学习技术的先进车牌识别解决方案。该系统整合了YOLOv5目标检测框架和LPRNet文本识别模型 1. YOLOv5目标检测框架 YOLO是一种先进的目标检测算法&#xff0c;以其实时性能和高精度闻名。YOLOv5是在前几代基础上进行优化的…

树莓派关机

文件 shutdown.sh #!/usr/bin/bash sudo shutdown -r nowpython 文件开头添加 #!/usr/bin/python3

Apache AGE 从文件导入图

您可以使用以下说明从文件创建图形。本文档介绍了&#xff1a; 包含从文件加载图形的函数的当前分支的信息使图形从文件创建的函数的说明作为输入的加载函数的CSV文件的结构&#xff0c;以及相关的注意事项 以及从文件加载国家和城市的简单源代码示例。 用户可以通过两个步骤…

从课本上面开始学习的51单片机究竟有什么特点,在现在的市场上还有应用吗?

引言 51单片机&#xff0c;作为一种经典的微控制器&#xff0c;被广泛应用于各种嵌入式系统中。尽管如今ARM架构的高性能低成本单片机在市场上占据主导地位&#xff0c;但51单片机凭借其独特的优势依然在某些领域保持着应用价值。本文将深入探讨51单片机的特点、架构、应用以及…

信必优收到著名生命科学前沿客户表扬信

近日&#xff0c;信必优收到著名生命科学前沿客户表扬信&#xff0c;客户表扬信必优员工在岗位上勤奋敬业、积极主动&#xff0c;圆满完成了既定的工作任务&#xff0c;在多个项目上展现出卓越技术能力和团队合作精神&#xff1b;其对工作的热情和对质量的追求给整个团队树立了…

WEB07Vue+Ajax

1. Vue概述 Vue&#xff08;读音 /vjuː/, 类似于 view&#xff09;&#xff0c;是一款用于构建用户界面的渐进式的JavaScript框架&#xff08;官方网站&#xff1a;https://cn.vuejs.org&#xff09;。 在上面的这句话中呢&#xff0c;出现了三个词&#xff0c;分别是&#x…

05:中断

中断 1、定时器T0中断1.1、定时器中断触发1.2、案例&#xff1a;通过定时器T0中断来实现灯间隔1s亮灭 2、外部中断2.1、外部中断的触发2.2、案例&#xff1a;使用外部中断0通过震动传感器控制LED1的亮灭 1、当中央处理机CPU正在处理某件事的时候外界发生了紧急事件请求&#xf…

Linux 扩展硬盘容量

根分区的硬盘容量不够了需要添加容量 扩展硬盘容量前提是需要虚拟机关机才能进行以下操作 在虚拟中找到虚拟机设置 >> 点击硬盘 >> 选择扩展 >> 输入自已要扩展的大小 >> 确定 这些设置好之后&#xff0c;启动虚拟机 fdisk /dev/sda n p 三个回车…