springboot3整合redis

打印 上一主题 下一主题

主题 751|帖子 751|积分 2253

来源于https://www.bilibili.com/video/BV1UC41187PR/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=865f32e12aef524afb83863069b036aa
一、整合redis

1.创建项目文件


2.添加依赖

  1. <dependencies>
  2.         <dependency>
  3.             <groupId>org.springframework.boot</groupId>
  4.             <artifactId>spring-boot-starter-web</artifactId>
  5.         </dependency>
  6.         <dependency>
  7.             <groupId>org.springframework.boot</groupId>
  8.             <artifactId>spring-boot-starter-data-redis</artifactId>
  9.         </dependency>
  10.         <dependency>
  11.             <groupId>com.mysql</groupId>
  12.             <artifactId>mysql-connector-j</artifactId>
  13.         </dependency>
  14.         <dependency>
  15.             <groupId>org.projectlombok</groupId>
  16.             <artifactId>lombok</artifactId>
  17.             <optional>true</optional>
  18.         </dependency>
  19.         <dependency>
  20.             <groupId>org.springframework.boot</groupId>
  21.             <artifactId>spring-boot-starter-test</artifactId>
  22.             <scope>test</scope>
  23.         </dependency>
  24.         <dependency>
  25.             <groupId>com.baomidou</groupId>
  26.             <artifactId>mybatis-plus-boot-starter</artifactId>
  27.             <version>3.5.5</version>
  28.         </dependency>
  29.         <!-- mybatis-plus会自动调用mybatis,但mybatis-spring版本和springboot3+版本的spring会不匹配,所以要升版本-->
  30.         <dependency>
  31.             <groupId>org.mybatis</groupId>
  32.             <artifactId>mybatis-spring</artifactId>
  33.             <version>3.0.3</version>
  34.         </dependency>
  35.     </dependencies>
复制代码
3.配置文件

后缀名改为yml
配置redis、mysql数据源、日记
  1. server:
  2.   port: 9999
  3. spring:
  4.   data:
  5.     redis:
  6.       port: 6379
  7.       host: localhost
  8.   datasource:
  9.     username: root
  10.     password: *****
  11.     url: jdbc:mysql:///testdb
  12.    
  13. logging:
  14.   level:
  15.     com.qqcn: debug
复制代码
4.创建redis配置类

@EnableCaching该注解为启动缓存管理
redisTemplate对象用来操作redis
redisCacheManager缓存管理器
此中setKey(Serializer)序列化 存到redis中的数据更加直观
  1. package com.qqcn.config;
  2. import org.springframework.cache.annotation.EnableCaching;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.data.redis.cache.RedisCacheConfiguration;
  6. import org.springframework.data.redis.cache.RedisCacheManager;
  7. import org.springframework.data.redis.cache.RedisCacheWriter;
  8. import org.springframework.data.redis.connection.RedisConnectionFactory;
  9. import org.springframework.data.redis.core.RedisTemplate;
  10. import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
  11. import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
  12. import org.springframework.data.redis.serializer.RedisSerializationContext;
  13. import org.springframework.data.redis.serializer.StringRedisSerializer;
  14. @Configuration
  15. @EnableCaching
  16. public class RedisConfig {
  17.     @Bean
  18.     public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
  19.         RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
  20.         redisTemplate.setConnectionFactory(factory);
  21.         redisTemplate.setKeySerializer(new StringRedisSerializer());
  22.         redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
  23.         redisTemplate.setHashKeySerializer(new StringRedisSerializer());
  24.         redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
  25.         return redisTemplate;
  26.     }
  27.     @Bean
  28.     public RedisCacheManager redisCacheManager(RedisTemplate<String, Object> redisTemplate) {  // 明确指定参数化类型
  29.         if (redisTemplate == null || redisTemplate.getConnectionFactory() == null) {
  30.             // 处理错误情况,例如抛出异常或采取默认行为
  31.             throw new RuntimeException("RedisTemplate or its connection factory is null");
  32.         }
  33.         RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory());
  34.         RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
  35.                 .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()));
  36.         return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
  37.     }
  38. }
复制代码

二、RedisTemplate操作-String

创建测试类演示


1.如果现在有一个登录的token想要存在redis里面

设置了有效期
  1. package com.qqcn;
  2. import org.junit.jupiter.api.Test;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.boot.test.context.SpringBootTest;
  5. import org.springframework.data.redis.core.RedisTemplate;
  6. import java.util.UUID;
  7. import java.util.concurrent.TimeUnit;
  8. @SpringBootTest
  9. public class RedisTest {
  10.     @Autowired
  11.     private RedisTemplate<String,Object> redisTemplate;
  12.     @Test
  13.     public void testString(){
  14.         String key = "user:token:0001";
  15. //      实际当中可以用jwt
  16.         redisTemplate.opsForValue().set(key, UUID.randomUUID().toString(),30, TimeUnit.MINUTES);
  17.         System.out.println(redisTemplate.opsForValue().get(key));
  18.     }
  19. }
复制代码
启动redis

启动乐成

token已存入


2.如果我们要对一篇文章进行访问数的统计

每检察一次都进行计数
  1. @Test
  2.     public void testString2(){
  3.         String key = "article:A00001:viewsCount";
  4.         redisTemplate.opsForValue().increment(key);
  5.         System.out.println(redisTemplate.opsForValue().get(key));
  6.     }
复制代码
第一次执行是1,第二次就是2了



3.如果想要存储一个对象

如果一个对象无需修改而是仅查询作用,那么存String也是一个很好的选择
在配置类中做了一个json的序列化处理,所以存储对象进去最终会被转换成json字符串
  1. @Test
  2.     public void testString3(){
  3.         Map<String,Object> user = new HashMap<>();
  4.         user.put("id","0001");
  5.         user.put("name","张三丰");
  6.         user.put("age",28);
  7.         String key = "user:0001";
  8.         redisTemplate.opsForValue().set(key,user);
  9.         System.out.println(redisTemplate.opsForValue().get(key));
  10.     }
复制代码
可以看到被存储为json格式




三、RedisTemplate操作-Hash

好比说在做一个电商类项目的时间,会有一个购物车对象
Hash是kv键值类型,值类型类似map结构,更得当用来生存对象
如果用opsForHash().put(Object key, Object hashkey, Object value)此中hashkey表示右边的k,value表示右边的v,如果一个一个存比力繁琐
所以我们这里用opsForHash().putAll(Object key,Map m)

测试取出购物车有哪些商品
  1. @Test
  2.     public void testHash(){
  3.         String key = "user:0001:cart";
  4.         Map<String, Object> shoppingCart = new HashMap<>();
  5.         shoppingCart.put("cartId", "123456789");
  6.         shoppingCart.put("userId", "987654321");
  7.         List<Map<String, Object>> items = List.of(
  8.                 Map.of("itemId", "1", "itemName", "手机", "price", 999.99, "quantity", 1),
  9.                 Map.of("itemId", "2", "itemName", "笔记本电脑", "price", 1499.99, "quantity",
  10.                         2),
  11.                 Map.of("itemId", "3", "itemName", "耳机", "price", 49.99, "quantity", 3)
  12.         );
  13.         shoppingCart.put("items", items);
  14.         shoppingCart.put("totalAmount", 3149.92);
  15.         shoppingCart.put("creationTime", "2046-03-07T10:00:00");
  16.         shoppingCart.put("lastUpdateTime", "2046-03-07T12:30:00");
  17.         shoppingCart.put("status", "未结账");
  18.         redisTemplate.opsForHash().putAll(key, shoppingCart);
  19.         System.out.println(redisTemplate.opsForHash().get(key, "items"));
  20.     }
复制代码



四、RedisTemplate操作-Set

是数据库中一种无序的、不重复的数据结构,用于存储一组唯一的元素
好比想去存储某个用户的粉丝有哪些人
redis中统计黑白常的快的
添加了三个粉丝add进去
  1. @Test
  2.     public void testSet(){
  3.         String key = "author:0001:fans";
  4.         redisTemplate.opsForSet().add(key,"张三","李四","王五");
  5.         System.out.println("粉丝量:" + redisTemplate.opsForSet().size(key));
  6.     }
复制代码
结果为粉丝量为3




五、RedisTemplate操作-ZSet

是一个有序且唯一的集合,每个元素都与一个浮点数分数干系联,使得集合中的元素可以根据分数进行排序,默认按照分数进行升序排序。
若要降序查询:
redisTemplate.opsForZSet().reverseRange(key,0,-1)
添加成员的时间有一个分数值,通过分数值进行排序处理
好比我们想存储某个用户的挚友列表
除了添加用户的信息,还可以添加时间,可以根据时间获取挚友的列表
  1. @Test
  2.     public void testZSet(){
  3.         String key = "user:0001:friends";
  4.         //取添加好友的时间的毫秒值
  5.         redisTemplate.opsForZSet().add(key,"张三",System.currentTimeMillis());
  6.         redisTemplate.opsForZSet().add(key,"李四",System.currentTimeMillis());
  7.         redisTemplate.opsForZSet().add(key,"王五",System.currentTimeMillis());
  8.         //倒序
  9.         Set<Object> set = redisTemplate.opsForZSet().reverseRange(key, 0, -1);
  10.         System.out.println(set);
  11.     }
复制代码
最后存入的在最前面(按时间的降序来处理)



六、RedisTemplate操作-List

一种简单的字符串列表,按照插入顺序排序
在redis中可以把List想象成通道,两边都是开放的,可以左进右出,也可以右进左出,或者左进左出也可以
可以用List实现队列的功能,好比现有一个订单的哀求处理,在买商品的时间很大概因为并发的题目引发别的题目,可以利用队列让订单做一个排队
存入数据:
redisTemplate.opsForList().leftPush(key,order1);
获取并移除数据:
redisTemplate.opsForList().rightPop(key)
  1. @Test
  2.     public void testList(){
  3.         String key = "order:queue";
  4.         //订单1
  5.         Map<String, Object> order1 = new HashMap<>();
  6.         order1.put("orderId", "1001");
  7.         order1.put("userId", "2001");
  8.         order1.put("status", "已完成");
  9.         order1.put("amount", 500.75);
  10.         order1.put("creationTime", "2024-08-09T09:30:00");
  11.         order1.put("lastUpdateTime", "2024-08-9T10:45:00");
  12.         order1.put("paymentMethod", "在线支付");
  13.         order1.put("shoppingMethod", "自提");
  14.         order1.put("remarks", "尽快处理");
  15.         //订单2
  16.         Map<String, Object> order2 = new HashMap<>();
  17.         order2.put("orderId", "1002");
  18.         order2.put("userId", "2002");
  19.         order2.put("status", "待处理");
  20.         order2.put("amount", 280.99);
  21.         order2.put("creationTime", "2024-08-09T11:00:00");
  22.         order2.put("lastUpdateTime", "2024-08-09T11:00:00");
  23.         order2.put("paymentMethod", "货到付款");
  24.         order2.put("shoppingMethod", "快递配送");
  25.         order2.put("remarks", "注意保鲜");
  26.         // A程序接收订单请求并将其加入队列
  27.         redisTemplate.opsForList().leftPush(key,order1);
  28.         redisTemplate.opsForList().leftPush(key,order2);
  29.         // B程序从订单队列中获取订单数据并处理
  30.         System.out.println("处理订单:" + redisTemplate.opsForList().rightPop(key));
  31.     }
复制代码
从右边取出的数据:订单1

还剩订单2



七、@RedisHash注解

除了用RedisTemplate来操作hash数据,还提供了@RedisHash注解
用于将java对象直接映射到redis的hash数据当中
java对象的存储和检索变得更加简单
关键步调:
1.@RedisHash注解标注在实体类上
2.创建接口并继续CrudRepository

1.创建一个实体类

@Id表示id为主键

  1. package com.qqcn.entity;
  2. import lombok.Data;
  3. import org.springframework.data.annotation.Id;
  4. import org.springframework.data.redis.core.RedisHash;
  5. @RedisHash
  6. @Data
  7. public class user {
  8.     @Id
  9.     private Integer id;
  10.     private String name;
  11.     private Integer age;
  12.     private String phone;
  13. }
复制代码

2.创建一个接口

想要用此注解还必要创建一个接口
继续CrudRepository(增删改查)设定泛型第一个是操作的实体,第二个是主键的类型
定义完了接口之后就具备了对对象的基于redis的增删改查的本事

  1. package com.qqcn.repository;
  2. import com.qqcn.entity.User;
  3. import org.springframework.data.repository.CrudRepository;
  4. public interface UserRedisRepository extends CrudRepository<User,Integer> {
  5. }
复制代码

3.测试类进行测试

注入UserRedisRepository,进行测试类测试
  1. @Autowired
  2.     private UserRedisRepository userRedisRepository;
  3.     @Test
  4.     public void testRedisHash(){
  5.         User user = new User();
  6.         user.setId(100);
  7.         user.setName("张三丰");
  8.         user.setAge(18);
  9.         user.setPhone("18899998888");
  10.         //保存到redis里面
  11.         userRedisRepository.save(user);
  12.         //读取
  13.         Optional<User> redisUser = userRedisRepository.findById(100);
  14.         System.out.println(redisUser);
  15.         //更新
  16.         user.setPhone("18899998866");
  17.         //看数据是否存在,存在就修改,不存在就保存
  18.         userRedisRepository.save(user);
  19.         //再打印一次
  20.         Optional<User> redisUser2 = userRedisRepository.findById(100);
  21.         System.out.println(redisUser2);
  22.         //删除
  23.         //userRedisRepository.deleteById(100);
  24.         //查看id是否存在
  25.         boolean exists = userRedisRepository.existsById(100);
  26.         System.out.println(exists);
  27.     }
复制代码

测试结果

因为没有指定key,key默认是 路径:id



八、缓存管理注解

redis最常见的用途就是用作缓存
而springboot的缓存管理功能旨在帮助开发人员轻松地在应用步伐中利用缓存,以进步性能和响应速度,它提供了一套注解和配置,使得开发人员可以在方法级别上进行缓存控制,并且支持多种缓存存储提供步伐

@Cacheable一样平常被用于查询方法,先在redis看看有无数据,如果有就直接在缓存里面拿,如果没有就查询数据库,并且会将查询到的数据放入缓存
@CachePut主要被用于更新缓存,一样平常大概用在新增和修改的方法上面
@CacheEvict主要用于扫除缓存,一样平常会用在删除方法的上面

1.预备表

  1. CREATE TABLE product (
  2.     id INT AUTO_INCREMENT PRIMARY KEY,
  3.     name VARCHAR(255) NOT NULL,
  4.     description TEXT,
  5.     price DECIMAL(10, 2) NOT NULL,
  6.     stock INT NOT NULL
  7. );
复制代码
  1. INSERT INTO product (name, description, price, stock) VALUES
  2. ('iPhone 15', '最新的iPhone型号', 8999.99, 100),
  3. ('三星Galaxy S24', '旗舰安卓手机', 7899.99, 150),
  4. ('MacBook Pro', '专业人士的强大笔记本电脑', 15999.99, 50),
  5. ('iPad Air', '性能强劲的便携式平板电脑', 5599.99, 200),
  6. ('索尼PlayStation 6', '下一代游戏机', 4499.99, 75);
复制代码



2.根据表创建实体类

  1. package com.qqcn.entity;
  2. import com.baomidou.mybatisplus.annotation.TableId;
  3. import com.baomidou.mybatisplus.annotation.TableName;
  4. import lombok.Data;
  5. @Data
  6. @TableName
  7. public class Product {
  8.     @TableId
  9.     private Integer id;
  10.     private String name;
  11.     private String description;
  12.     private Double price;
  13.     private Integer stock;
  14. }
复制代码

3.创建一个mapper接口

到数据库当中查数据,必须要有数据库操作的存在
前面创建项目的时间是添加了mybatis-plus的依赖
所以创建一个mapper来简化数据库的操作
在mybatis-plus里面想要实现增删改查必要在接口上面继续BaseMapper,泛型里面指定实体是谁
  1. package com.qqcn.mapper;
  2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  3. import com.qqcn.entity.Product;
  4. public interface ProductMapper extends BaseMapper<Product> {
  5. }
复制代码
注意:要在启动类上加一个注解@MapperScan扫描一下


4.创建一个service

在此创建增删改查的几个方法
  1. package com.qqcn.service;
  2. import com.qqcn.entity.Product;
  3. public interface ProductService {
  4.     //查询
  5.     public Product getProductById(Integer id);
  6.     //新增  还是要写一个返回值
  7.     //因为我们要用注解@CachePut缓存数据是只能缓存方法的返回值
  8.     public Product addProduct(Product product);
  9.     //修改
  10.     public Product updateProduct(Product product);
  11.     //删除
  12.     public void deleteProductById(Integer id);
  13. }
复制代码

5.创建相应的实现类

  1. package com.qqcn.service.impl;
  2. import com.qqcn.entity.Product;
  3. import com.qqcn.mapper.ProductMapper;
  4. import com.qqcn.service.ProductService;
  5. import jakarta.annotation.Resource;
  6. import org.springframework.stereotype.Service;
  7. @Service
  8. public class ProductServiceImpl implements ProductService {
  9.     @Resource
  10.     private ProductMapper productMapper;
  11.     @Override
  12.     public Product getProductById(Integer id) {
  13.         return productMapper.selectById(id);
  14.     }
  15.     @Override
  16.     public Product addProduct(Product product) {
  17.         productMapper.insert(product);
  18.         return product;
  19.     }
  20.     @Override
  21.     public Product updateProduct(Product product) {
  22.         productMapper.updateById(product);
  23.         return product;
  24.     }
  25.     @Override
  26.     public void deleteProductById(Integer id) {
  27.         productMapper.deleteById(id);
  28.     }
  29. }
复制代码

6.添加缓存存储的注解

我们希望看到的是
首先查找是先从redis里面找数据,如果有的话sql就不执行,如果sql执行了的话,日记里面会体现
value指定缓存的名称,key是生存在redis中的键(必要拼接id用单引号)
设置之后增删改查的同时会作缓存同步的处理,作高频访问的时间,可以进步效率,同时降低mysql压力
  1. package com.qqcn.service.impl;
  2. import com.qqcn.entity.Product;
  3. import com.qqcn.mapper.ProductMapper;
  4. import com.qqcn.service.ProductService;
  5. import jakarta.annotation.Resource;
  6. import org.springframework.cache.annotation.CacheEvict;
  7. import org.springframework.cache.annotation.CachePut;
  8. import org.springframework.cache.annotation.Cacheable;
  9. import org.springframework.stereotype.Service;
  10. @Service
  11. public class ProductServiceImpl implements ProductService {
  12.     @Resource
  13.     private ProductMapper productMapper;
  14.     @Cacheable(value = "product", key = "'product:' + #id")
  15.     @Override
  16.     public Product getProductById(Integer id) {
  17.         return productMapper.selectById(id);
  18.     }
  19.     @CachePut(value = "product", key = "'product:'+ #product.id")
  20.     @Override
  21.     public Product addProduct(Product product) {
  22.         productMapper.insert(product);
  23.         return product;
  24.     }
  25.     @CachePut(value = "product", key = "'product:'+ #product.id")
  26.     @Override
  27.     public Product updateProduct(Product product) {
  28.         productMapper.updateById(product);
  29.         return product;
  30.     }
  31.     @CacheEvict(value = "product", key = "'product:' + #id")
  32.     @Override
  33.     public void deleteProductById(Integer id) {
  34.         productMapper.deleteById(id);
  35.     }
  36. }
复制代码

7.测试类进行测试

(1)测试查询

  1. @Autowired
  2.     private ProductService productService;
  3.     @Test
  4.     public void testQuery(){
  5.         Product product = productService.getProductById(1);
  6.         System.out.println(product);
  7.     }
复制代码
这里在执行时发生了一个错误java.sql.SQLException: Access denied for user ‘root‘@‘localhost‘ 
堕落在于mysql的密码是数字同时用的是.yml文件 所以我们必要把密码用双引号引起来
  1. spring:
  2.   data:
  3.     redis:
  4.       port: 6379
  5.       host: localhost
  6.   datasource:
  7.     username: root
  8.     password: "*****"
  9.     url: jdbc:mysql://localhost:3306/testdb
复制代码

更改之后测试乐成,测试结果如下

redis中已缓存

重新测第二次
没有sql语句,因为我们可以在缓存中读取了


(2)测试更新(和新增同理,这里只演示更新)

  1. @Test
  2.     public void testUpdate(){
  3.         //将刚刚查询到的名字修改一下
  4.         Product product = productService.getProductById(1);
  5.         product.setName("苹果15");
  6.         //这里更新表一定会有sql日志,更新了缓存
  7.         productService.updateProduct(product);
  8.         //修改之后再一次查询,这里就没有sql日志了
  9.         Product product2 = productService.getProductById(1);
  10.         System.out.println(product2);
  11.     }
复制代码
测试结果,只有中间更新的时间有sql日记,查询走的都是缓存

redis已同步更改


(3)测试删除

删完的同时,缓存中的数据也会不存在
  1. @Test
  2.     public void testDelete(){
  3.         productService.deleteProductById(1);
  4.     }
复制代码
测试结果,会打印一个删除的sql日记

mysql数据表中该数据被删除

redis中的缓存也没有了


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

何小豆儿在此

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表