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"}) //代表这个数据是可以缓存的 当前方法的结果需要缓存,如果缓存中有,方法不用调用。//如果缓存中没有,调用方法,将方法结果放入缓存@Overridepublic 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*/@Beanpublic 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 感知数据库去更新 缓存

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

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

相关文章

二分法binary search

欢迎来到一夜看尽长安花 博客&#xff0c;您的点赞和收藏是我持续发文的动力 对于文章中出现的任何错误请大家批评指出&#xff0c;一定及时修改。有任何想要讨论的问题可联系我&#xff1a;3329759426qq.com 。发布文章的风格因专栏而异&#xff0c;均自成体系&#xff0c;不足…

解决一下git clone失败的问题

1&#xff09;.不开梯子&#xff0c;我们用https克隆 git clone https://github.com 报错&#xff1a; Failed to connect to github.com port 443 after 2091 ms: Couldnt connect to server 解决办法&#xff1a; 开梯子&#xff0c;然后# 注意修改成自己的IP和端口号 gi…

docker搭建普罗米修斯监控gpu

ip8的服务器监控ip110和ip111的服务器 被监控的服务器110和111只需要安装node-export和nvidia-container-toolkit 下载镜像包 docker pull prom/node-exporter docker pull prom/prometheus docker pull grafana/grafana新建目录 mkdir /opt/prometheus cd /opt/prometheus/…

生信软件27 - 基于python的基因注释数据查询/检索库mygene

1. mygene库简介 MyGene.info提供简单易用的REST Web服务来查询/检索基因注释数据&#xff0c;具有以下特点&#xff1a; mygene技术文档&#xff1a; https://docs.mygene.info/en/latest/ 多物种支持: 包括人、小鼠、大鼠、斑马鱼等多个模式生物&#xff1b; 多数据源聚合…

卷积神经网络图像识别车辆类型

卷积神经网络图像识别车辆类型 1、图像 自行车: 汽车: 摩托车: 2、数据集目录 3、流程 1、获取数据,把图像转成矩阵,并随机划分训练集、测试集 2、把标签转为数值,将标签向量转换为二值矩阵 3、图像数据归一化,0-1之间的值 4、构造卷积神经网络 5、设置图像输入…

记录些MySQL题集(8)

ACID原则、事务隔离级别及事务机制原理 一、事务的ACID原则 什么是事务呢&#xff1f;事务通常是由一个或一组SQL组成的&#xff0c;组成一个事务的SQL一般都是一个业务操作&#xff0c;例如聊到的下单&#xff1a;「扣库存数量、增加订单详情记录、插入物流信息」&#xff0…

Qt5.12.2安装教程

文章目录 文章介绍下载连接安装教程 文章介绍 安装Qt5.12.2 下载连接 点击官网下载 安装包下载完毕 安装教程 点开设置&#xff0c;添加临时储存库&#xff0c;复制连接“https://download.qt.io/online/qtsdkrepository/windows_x86/root/qt/” 点击测试&#xff0…

set类和map类介绍和简单使用

目录 set类介绍与简单使用 set类 multiset类 map类介绍与简单使用 map类 multimap类 set类介绍与简单使用 set类是一种关联式容器&#xff0c;在数据检索时比序列式容器效率更高。本质是一个常规的二叉搜索树&#xff0c;但是为了防止出现单支树导致效率下降进行了相关优…

【Linux】将IDEA项目部署到云服务器上,让其成为后台进程(保姆级教学,满满的干货~~)

目录 部署项目到云服务器什么是部署一、 创建MySQL数据库二、 修改idea配置项三、 数据打包四、 部署云服务器五、开放端口号六 、 验证程序 部署项目到云服务器 什么是部署 ⼯作中涉及到的"环境" 开发环境:开发⼈员写代码⽤的机器.测试环境:测试⼈员测试程序使⽤…

SQL面试题-留存率计算

表定义&#xff1a; create table if not exists liuliang_detail (user_id string comment ,record_time string comment yyyymmdd hh:mi:ss ) comment 流量明细表 ; 方法一&#xff1a; 计算的是整段时间范围内&#xff0c;每一天为基准的所有的留存1、2、7天的用户数。 …

WEB前端05-JavaScrip基本对象

JavaScript对象 1.Function对象 函数的创建 //方法一&#xff1a;自定义函数 function 函数名([参数]) {函数体[return 表达式] }//方法二&#xff1a;匿名函数 (function([参数]) {函数体[return 表达式] }); **使用场景一&#xff1a;定义后直接调用使用(只使用一次) (fun…

成为git砖家(1): author 和 committer 的区别

大家好&#xff0c;我是白鱼。一直对 git author 和 committer 不太了解&#xff0c; 今天通过 cherry-pick 的例子搞清楚了区别。 原理 例如我克隆了著名开源项目 spdlog 的源码&#xff0c; 根据某个历史 commit A 创建了分支&#xff0c; 然后 cherry-pick 了这个 commit …

《系统架构设计师教程(第2版)》第11章-未来信息综合技术-06-云计算(Cloud Computing) 技术概述

文章目录 1. 相关概念2. 云计算的服务方式2.1 软件即服务 (SaaS)2.2 平台即服务 (PaaS)2.3 基础设施即服务 (IaaS)2.4 三种服务方式的分析2.4.1 在灵活性2.4.2 方便性方 3. 云计算的部署模式3.1 公有云3.2 社区云3.3 私有云3.4 混合云 4. 云计算的发展历程4.1 虚拟化技术4.2 分…

python课设——宾馆管理系统

python课设——宾馆管理系统 数据库课设-宾馆管理系统-python3.7pyqt5 简介 大二数据库课程设计&#xff08;3-4天工作量&#xff09;的项目&#xff0c;登录界面的ui设计参考了他人成果&#xff0c;其余ui以及所有后端部分全部独立完成&#xff0c;详细功能见功能模块图使用…

防火墙NAT地址转换和智能选举综合实验

一、实验拓扑 目录 一、实验拓扑 二、实验要求&#xff08;接上一个实验要求后&#xff09; 三、实验步骤 3.1办公区设备可以通过电信链路和移动链路上网(多对多的NAT&#xff0c;并且需要保留一个公网IP不能用来转换) 3.2分公司设备可以通过总公司的移动链路和电信链路访…

简单爬虫案例

准备工作&#xff1a; 1. 安装好python3 最低为3.6以上&#xff0c; 并成功运行pyhthon3 程序 2. 了解python 多进程原理 3. 了解 python HTTP 请求库 requests 的基本使用 4. 了解正则表达式的用法和python 中 re 库的基本使用 爬取目标 目标网站&#xff1a; https://…

scrapy框架爬取豆瓣top250电影排行榜(下)

&#xff08;3&#xff09;在 pipeline.py 文件中对数据进行存储&#xff0c;此程序先写 入 txt 文件中&#xff0c;是为了判断该程序是否能正确爬取出数据。 此处使用了 json 库&#xff0c;使用 ensure_ascii False&#xff0c;能够确 保非 ASCII 字符&#xff08;如中文&am…

贪心算法(2024/7/16)

1合并区间 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 示例 1&#xff1a; 输入&#xff1a;inter…

SpringCloud网关的实现原理与使用指南

Spring Cloud网关是一个基于Spring Cloud的微服务网关&#xff0c;它是一个独立的项目&#xff0c;可以对外提供API接口服务&#xff0c;负责请求的转发和路由。本文将介绍Spring Cloud网关的实现原理和使用指南。 一、Spring Cloud网关的实现原理 Spring Cloud网关基于Spring…

5.操作led

模版使用之前的hello驱动程序。 想要操作led&#xff0c;首先要找到原理图&#xff0c;查找GPIO对应的GPIO引脚 从图中能看出来LED2对应的GPIO是GPIO5_3&#xff0c;同时可以得知这个LED2是低电平点亮。查看cat /sys/kernel/debug/gpio可得知GPIO5_3&#xff08;第四组GPIO的第…