10 Redis之SB整合Redis

7. SB整合Redis

Spring Boot 中可以直接使用 Jedis 实现对 Redis 的操作,但一般不这样用,而是使用 Redis操作模板 RedisTemplate 类的实例来操作 Redis。

RedisTemplate 类是一个对 Redis 进行操作的模板类。该模板类中具有很多方法,这些方法很多与 Redis 操作命令同名或类似。例如,delete()、keys()、scan(),还有事务相关的 multi()、exec()、discard()、watch()等。当然还有获取对各种 Value 类型进行操作的操作实例的两类方法 boundXxxOps(k)与 opsForXxx()。

7.1 需求

对于一个资深成熟的金融产品交易平台,其用户端首页一般会展示其最新金融产品列表,同时还为用户提供了产品查询功能。
另外,为了显示平台的实力与信誉,在平台首页非常显眼的位置还会展示平台已完成的总交易额与注册用户数量。

对于管理端,管理员可通过管理页面修改产品、上架新产品、下架老产品。

为了方便了解 Redis 与 Spring Boot 的整合流程,这里对系统进行了简化:用户端首页仅提供根据金融产品名称的查询,显眼位置仅展示交易总额。
管理端仅实现上架新产品功能。

7.2 准备

  1. 创建一个SB工程 , JDK8, SB版本2.7.4
  2. 导入依赖
  3. resource下创建一个文件夹webapp
  4. 创建一张product表
  5. 编写yaml文件
    <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.4</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.sunsplanter</groupId><artifactId>SB-Redis</artifactId><version>0.0.1-SNAPSHOT</version><name>SB-Redis</name><description>SB-Redis</description><properties><java.version>17</java.version></properties><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.2</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.12</version></dependency><dependency>www.bjpowernode.com 187 / 248 Copyright© 动力节点<groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-jasper</artifactId></dependency><dependency><groupId>javax.servlet</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><resources><resource><directory>src/main/java</directory><includes><include>**/*.xml</include></includes></resource><resource><directory>src/main/webapp</directory><targetPath>META-INF/resources</targetPath><includes><include>**/*.*</include></includes></resource></resources></build>
</project>
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(20) DEFAULT NULL,`rate` double DEFAULT NULL,`amount` double DEFAULT NULL,`raised` double DEFAULT NULL,`cycle` int(11) DEFAULT NULL,`endTime` char(10) DEFAULT '0',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
www.bjpowernode.com 190 / 248 Copyright© 动力节点
INSERT INTO `product` VALUES (1,'天鑫添益 2',2.76,50000,20000,30,'2022-07-10'),(2,'国
泰添益',2.86,30000,30000,60,'2022-07-12'),(3,'国泰高鑫
',2.55,60000,50000,90,'2022-07-09'),(4,'国福民安
',2.96,30000,20000,7,'2022-05-10'),(5,'天益鑫多
',2.65,80000,60000,20,'2022-07-05'),(6,'惠农收益
',3.05,30000,20000,10,'2022-06-10'),(7,'惠农三鑫
',2.76,50000,30000,30,'2022-07-02'),(8,'励学收益',2.86,30000,20000,20,'2022-07-11');
mybatis:mapper-locations: classpath:com/sunsplanter/dao/*.xmltype-aliases-package: com.sunsplanter.beanspring:datasource:type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://localhost:3306/tanhua?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMTusername: rootpassword: ???driver-class-name: com.mysql.cj.jdbc.DriverLogging:pattern:console: level-%-5level - %msg%n

7.3 编写代码

在这里插入图片描述

7.3.1 controller

package com.sunsplanter.controller;@Controller
public class ProductController {@Autowiredprivate ProductService service;@GetMapping("/")public String indexHandle(Model model) {// 查询总交易额Double turnover = service.findTurnover();model.addAttribute("turnover", turnover);//查询所有产品List<Product> allProducts = service.findAllProducts();model.addAttribute("allProducts", allProducts);return "/index.jsp";}// 上架新产品,然后返回管理页面@PostMapping("/product/")public String registerHandle(Product product, Model model) {//添加新产品service.saveProduct(product);// 查询所有产品List<Product> allProducts = service.findAllProducts();model.addAttribute("allProducts", allProducts);return "/jsp/manager.jsp";}// 根据产品名称查询产品@GetMapping("/product/name")public String listHandle(String name, Model model) {List<Product> result = service.findProductsByName(name);model.addAttribute("result", result);model.addAttribute("name", name);return "/jsp/result.jsp";}
}

7.3.2 service

package com.sunsplanter.service;public interface ProductService {void saveProduct(Product product);List<Product> findAllProducts();Double findTurnover();List<Product> findProductsByName(String name);
}package com.sunsplanter.service.impl;@Service
public class ProductServiceImpl implements ProductService {@Autowiredprivate ProductDao dao;@Override@Transactional(rollbackFor = Exception.class)public void saveProduct(Product product) {dao.insertProduct(product);}@Overridepublic List<Product> findProductsByName(String name) {return dao.selectProductsByName(name);}@Overridepublic List<Product> findAllProducts() {return dao.selectAllProducts();}@Overridepublic Double findTurnover() {//获取当前日期并格式化Date date =new Date();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");//从DB中查询turnoverObject turnover = dao.selectTurnover(sdf.format(date));return (Double) turnover;}
}

7.3.3 bean

package com.sunsplanter.bean;@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product {private Integer id;private String name;//年华利率private Double rate;//募集总额private Double amount;//已募集金额private Double raised;//产品募集结束时间private Integer cycle;private String endtime;
}

7.3.4 dao接口和mapper.xml

package com.sunsplanter.dao;@Mapper
public interface ProductDao {void insertProduct(Product product);List<Product> selectAllProducts();List<Product> selectProductsByName(String name);Object selectTurnover(String date);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sunsplanter.dao.ProductDao"><insert id="insertProduct">insert into product(name, rate, amount, raised, cycle, endTime)values (#{name}, #{rate}, #{amount}, #{raised}, #{cycle}, #{endTime})</insert><select id="selectAllProducts" resultType="Product">select id,name,rate, amount,raised, cycle,endTimefrom product</select><select id="selectProductsByName" resultType="Product">select id,name,rate,amount,raised, cycle,endTimefrom productwhere name like '%'#{xxx}'%'</select><select id="selectTurnover" resultType="double">select sum(raised)from productwhere endTime is not null and endTime &lt; #{xxx}
</select></mapper>

7.4 测试与问题

以上是一个纯SpringBoot项目, 并未整合任何Redis.以下示例

7.5 整合Redis

Spring Boot 与 Redis 整合 大体上为以下几步

  1. 在 POM 中导入依赖
  2. 在配置文件中注册 Redis 连接信息与缓存信息
  3. 需要缓存到 Redis 中的实体类必须要序列化
  4. Spring Boot 启动类中要添加@EnableCaching 注解
  5. 查询方法上要添加@Cacheable 注解
  6. 对数据进行写操作的方法上添加@CacheEvict 注解
  7. 对于需要手工操作 Redis 的方法,需通过 RedisTemplate 来获取操作对象

7.5.0 学习SB中的Redis操作

7.5.0.1 @Cacheable

@Cacheable可以标记在一个方法上,也可以标记在一个类上。
当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。

对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。

Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果,至于键的话,Spring又支持两种策略,默认策略和自定义策略,这个稍后会进行说明。需要注意的是当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的。

	常用的属性cacheNames/value :用来指定缓存组件的名字key :缓存数据时使用的 key,可以用它来指定。默认是使用方法参数的值。(这个 key 你可以使用 spEL 表达式来编写)condition :可以用来指定符合条件的情况下才缓存keyGenerator :key 的生成器。 key 和 keyGenerator 二选一使用cacheManager :可以用来指定缓存管理器。从哪个缓存管理器里面获取缓存。unless :否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。当然你也可以获取到结果进行判断。(通过 #result 获取方法结果)sync :是否使用异步模式。
  1. value属性指定Cache名称

    value属性是必须指定的,其表示当前方法的返回值是会被缓存在哪个Cache上的,对应Cache的名称。其可以是一个Cache也可以是多个Cache,当需要指定多个Cache时其是一个数组。

   @Cacheable("cache1")//Cache是发生在cache1上的public User find(Integer id) {return null;}
  1. key属性自定义key

    该属性支持SpringEL表达式。
    当我们没有指定该属性时,Spring将使用默认策略生成key。
    

我们这里先来看看自定义策略,至于默认策略会在后文单独介绍。
自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。下面是几个使用参数作为key的示例。

   @Cacheable(value="users", key="#id")public User find(Integer id) {returnnull;}@Cacheable(value="users", key="#p0")public User find(Integer id) {returnnull;}@Cacheable(value="users", key="#user.id")public User find(User user) {returnnull;}@Cacheable(value="users", key="#p0.id")public User find(User user) {returnnull;}

除了上述使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。通过该root对象我们可以获取到以下信息。

在这里插入图片描述

   当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。如:
   @Cacheable(value={"users", "xxx"}, key="caches[1].name")public User find(User user) {returnnull;}
  1. condition属性指定发生的条件
    有的时候我们可能并不希望缓存一个方法所有的返回结果。通过condition属性可以实现这一功能。
    condition属性默认为空,表示将缓存所有的调用情形。
    其值是通过SpringEL表达式来指定的,当为true时表示进行缓存处理;当为false时表示不进行缓存处理,即每次调用该方法时该方法都会执行一次。如下示例表示只有当user的id为偶数时才会进行缓存。
 @Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0")public User find(User user) {System.out.println("find user by user " + user);return user;}
7.5.0.2 @CacheEvict
   @CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的**执行后**都会触发缓存的清除操作。@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性类似。即value表示清除操作是发生在哪些Cache上的(对应Cache的名称);key表示需要清除的是哪个key,如未指定则会使用默认策略生成的key;condition表示清除操作发生的条件。
  1. allEntries属性

    allEntries是boolean类型,表示是否需要清除缓存中的所有元素。默认为false,表示不需要。
    当指定了allEntries为true时,Spring Cache将删除所有除了指定的key以外的所有key。
    有的时候我们需要Cache一下清除所有的元素,这比一个一个清除元素更有效率。

   @CacheEvict(value="users", allEntries=true)public void delete(Integer id) {System.out.println("delete user by id: " + id);}
  1. beforeInvocation属性

    清除操作默认是在对应方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。
    使用beforeInvocation可以改变触发清除操作的时间,当我们指定该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素。

   @CacheEvict(value="users", beforeInvocation=true)public void delete(Integer id) {System.out.println("delete user by id: " + id);}

7.5.0.3 RedisTemplate常用集合

RedisTemplate常用集合

7.5.1 准备

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
redis:host:redisport:6379#Sentinel高可用集群
#sentinel:
#  master: mymaster
#  nodes: redis:26380,redis:26381,redis:26382#Cluster分布式系统
#cluster:
#  nodes: redis:6380,redis:6381,edis:6382,edis:6383,redis:6384,redis:6385cache:type: rediscache-names: pc

7.5.2 修改实体类

由于要将查询的实体类对象缓存到 Redis,Redis 要求实体类必须序列化。所以需要实体类实现序列化接口。

class Product implements Serializable {

7.5.3 修改serviceImpl类

//注入RedisTemplate模板
@Autowired
private RedisTemplate<0bject,0bject> rt;
//清除名为pc的缓存空间. 且清除其内的所有条目,如果想要保留一些, 则需通过 key 属性指定要清理的 key 数据。
//该注解会在方法体执行前执行
@0verride
@Transactional(rollbackFor=Exception.class)
@CacheEvict(value ="pc",allEntries = true)
public void saveProduct(Product product){dao.insertProduct(product);
}@Override
public Double findTurnover(){//绑定键为"turnover的键值对, 然后就可以对其进行操作"BoundValue0perations<Object, 0bject> ops = rt.boundValue0ps("turnover");Object turnover = ops.get();//先从缓存中查, 如果缓存中没有, 再从DB中获取, 最终写入缓存if(turnover == null){Date date = new Date();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd")turnover = dao.selectTurnover(sdf.format(date));//写入缓存ops.set(turnover,10,TimeUnit.SECONDS);}return (Double) turnover;
}

7.5.4 启动类增加注解

@EnableCaching 用于开启当前应用的缓存功能。

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

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

相关文章

【手机端测试】adb基础命令

一、什么是adb adb&#xff08;Android Debug Bridge&#xff09;是android sdk的一个工具 adb是用来连接安卓手机和PC端的桥梁&#xff0c;要有adb作为二者之间的维系&#xff0c;才能让用户在电脑上对手机进行全面的操作。 Android的初衷是用adb这样的一个工具来协助开发人…

el-submenu is-opened 展开/闭合;el-submenu is-opened保持一个子菜单的展开控制

写了个mes系统目录 点击子菜单展开后&#xff0c;上一级菜单没有默认关闭。主流后台管理系统大部分都是保持一个子菜单关闭状态、 问度娘无果后&#xff0c;查询官网&#xff0c;一个属性搞定。 unique-opened 是否只保持一个子菜单的展开 加在 <el-menu 组件上即可 完整代…

LeetCode_Java_动态规划系列(1)(题目+思路+代码)

目录 斐波那契类型 746.使用最小花费爬楼梯 矩阵 120. 三角形最小路径和 斐波那契类型 746.使用最小花费爬楼梯 给你一个整数数组 cost &#xff0c;其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用&#xff0c;即可选择向上爬一个或者两个台阶。…

Leetcode202. 快乐数中为什么一定会循环?

他提示中是2的31次方&#xff1a; 相当于我就当它10个9&#xff0c;那么通过一次变换变成了81*10 810&#xff0c;所以他以后最后的范围也只能在[0,810]之间&#xff0c;810是最大的&#xff0c;然后任意一个数x&#xff0c;通过810次变换都不重复&#xff0c;那么811次变换那…

GIS之深度学习01:检测电脑是否包含英伟达GPU

GPU&#xff08;Graphics processing unit&#xff09;&#xff0c;中文全称图形处理器&#xff0c;我们听说的更多的CPU全称是central processing unit&#xff0c;中央处理器。研究深度学习和神经网络大都离不开GPU&#xff0c;在GPU的加持下&#xff0c;我们可以更快的获得模…

【QT+QGIS跨平台编译】之五十二:【QGIS_CORE跨平台编译】—【qgsexpressionlexer.cpp生成】

文章目录 一、Flex二、生成来源三、构建过程一、Flex Flex (fast lexical analyser generator) 是 Lex 的另一个替代品。它经常和自由软件 Bison 语法分析器生成器 一起使用。Flex 最初由 Vern Paxson 于 1987 年用 C 语言写成。 “flex 是一个生成扫描器的工具,能够识别文本中…

Redis高可用三主三从集群部署(三种方式部署/18个节点的大集群)

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容使用宝塔面板搭建集群规划配置验证 使用docker搭建使用脚本搭建规划防火墙端口配置脚本redis.conf配置文件执行过程 &#x1f4e2;文章总结&#x1f4e5;博主目标 &#x1f50a;博主介绍 &#x1f31f;我是廖志伟&#xff…

c入门第二十五篇: 学生成绩管理系统优化(多线程)

前言 程夏&#xff1a;“你这个系统不好用啊&#xff1f;连上之后没有反应&#xff0c;是不是挂了&#xff1f;” 师弟&#xff1a;“不应该啊&#xff0c;我这边好好的&#xff0c;可以正常操作的。” 程夏&#xff1a;“我这边有问题&#xff0c;大概率还是你系统问题。你…

C语言标准库函数qsort( )——数据排序

大家好&#xff01;我是保护小周ღ&#xff0c;本期为大家带来的是深度解剖C语言标准库函数 qsort()&#xff0c;qsort()函数他可以对任意类型的数据排序&#xff0c;博主会详细解释函数使用方法&#xff0c;以及使用快速排序的左右指针法模拟实现函数功能&#xff0c;这样的排…

IT廉连看——C语言——分支语句

IT廉连看—分支语句 一、什么是语句 C语句可分为以下五类&#xff1a; 表达式语句 函数调用语句 控制语句 复合语句 空语句 本周后面介绍的是控制语句。 控制语句用于控制程序的执行流程&#xff0c;以实现程序的各种结构方式&#xff0c;它们由特定的语句定义符组成&…

Delphi 报错 Type androidx.collection.ArraySet is defined multiple times

Delphi 11 建立一个新的 Multi-Device Application 编译成app的时候报错 报错信息 [PAClient Error] Error: E7688 Unable to execute "E:\Program\Java\jdk1.8.0_301\bin\java.exe" -cp "e:\program\embarcadero\studio\22.0\bin\Android\r8-3.3.28.jar"…

FPGA_SD卡读写

一 SD卡 SD卡&#xff0c;安全数字卡&#xff0c;体积小&#xff0c;容量大&#xff0c;存储速度块&#xff0c;支持热插拔。 二 SD卡存储容量 SD卡类型协议规范容量等级SDSCSD1.0上限至2GBSDHCSD2.02GB至32GBSDXCSD3.032GB至2TB 三 SD卡速度等级 标志串列数据写入速度UHS…

MWC 2024丨美格智能发布全新5G-A模组及FWA解决方案,将5.5G带入现实

2月26日&#xff0c;在MWC 2024世界移动通信大会上&#xff0c;美格智能正式宣布推出5G-A模组SRM817WE以及全新的5G-A FWA解决方案&#xff0c;包含5G-A CPE解决方案SRT858M、5G-A MiFi解决方案SRT878H和5G-A ODU解决方案SRT853MX&#xff0c;旨在进一步提升网络性能&#xff0…

跟着cherno手搓游戏引擎【25】封装2DRenderer,封装shader传参,自定义Texture

封装2DRenderer&#xff1a; Renderer.h: #include"ytpch.h" #include"Renderer.h" #include <Platform/OpenGL/OpenGLShader.h> #include"Renderer2D.h" namespace YOTO {Renderer::SceneData* Renderer::m_SceneData new Renderer::S…

python中“全局变量”之谜

全局变量&#xff0c;是不是以为着在整个程序中的值都是一样的&#xff0c;是不是都是同一个呢&#xff1f; 我们通过下面的例子来看全局"变量之变” 上面的程序运行结果如下&#xff1a; 从上面的运行结果可以看出&#xff1a;尽管变量num被声明为全局变量&#xff0c;在…

测试基础2:接口测试入门儿 哟呼开心

单元测试&#xff1a;一段代码的功能是否正确&#xff0c;在软件开发中进行的测试活动&#xff1b; 单元测试框架&#xff1a; java&#xff1a;junit&#xff0c;testNG c#:NUint python&#xff1a;pytest、unittest接口测试&#xff1a;检测外部系统与系统之间以及内部各个子…

腾讯云4核8g云服务器能承受多少人访问?

腾讯云4核8G服务器支持多少人在线访问&#xff1f;支持25人同时访问。实际上程序效率不同支持人数在线人数不同&#xff0c;公网带宽也是影响4核8G服务器并发数的一大因素&#xff0c;假设公网带宽太小&#xff0c;流量直接卡在入口&#xff0c;4核8G配置的CPU内存也会造成计算…

MFC web文件 CHttpFile的使用初探

MFC CHttpFile的使用 两种方式&#xff0c;第一种OpenURL&#xff0c;第二种SendRequest&#xff0c;以前捣鼓过&#xff0c;今天再次整结果发现各种踩坑&#xff0c;好记性不如烂笔头&#xff0c;记录下来。 OpenURL 这种方式简单粗暴&#xff0c;用着舒服。 try {//OpenU…

图形系统开发实战课程:进阶篇(上)——7.图形交互操作: 视点控制与动画

图形开发学院&#xff5c;GraphAnyWhere 课程名称&#xff1a;图形系统开发实战课程&#xff1a;进阶篇(上)课程章节&#xff1a;“图形交互操作: 视点控制与动画”原文地址&#xff1a;https://www.graphanywhere.com/graph/advanced/2-7.html 第七章 图形交互操作: 视点控制与…

[云原生] 二进制安装K8S(中)部署网络插件和DNS

书接上文&#xff0c;我们继续部署剩余的插件 一、K8s的CNI网络插件模式 2.1 k8s的三种网络模式 K8S 中 Pod 网络通信&#xff1a; &#xff08;1&#xff09;Pod 内容器与容器之间的通信 在同一个 Pod 内的容器&#xff08;Pod 内的容器是不会跨宿主机的&#xff09;共享…