来源于https://www.bilibili.com/video/BV1UC41187PR/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=865f32e12aef524afb83863069b036aa
一、整合redis
1.创建项目文件
2.添加依赖
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
- <dependency>
- <groupId>com.mysql</groupId>
- <artifactId>mysql-connector-j</artifactId>
- </dependency>
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.baomidou</groupId>
- <artifactId>mybatis-plus-boot-starter</artifactId>
- <version>3.5.5</version>
- </dependency>
- <!-- mybatis-plus会自动调用mybatis,但mybatis-spring版本和springboot3+版本的spring会不匹配,所以要升版本-->
- <dependency>
- <groupId>org.mybatis</groupId>
- <artifactId>mybatis-spring</artifactId>
- <version>3.0.3</version>
- </dependency>
- </dependencies>
复制代码 3.配置文件
后缀名改为yml
配置redis、mysql数据源、日记
- server:
- port: 9999
- spring:
- data:
- redis:
- port: 6379
- host: localhost
- datasource:
- username: root
- password: *****
- url: jdbc:mysql:///testdb
-
- logging:
- level:
- com.qqcn: debug
复制代码 4.创建redis配置类
@EnableCaching该注解为启动缓存管理
redisTemplate对象用来操作redis
redisCacheManager缓存管理器
此中setKey(Serializer)序列化 存到redis中的数据更加直观
- package com.qqcn.config;
- 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.cache.RedisCacheManager;
- import org.springframework.data.redis.cache.RedisCacheWriter;
- import org.springframework.data.redis.connection.RedisConnectionFactory;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
- import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
- import org.springframework.data.redis.serializer.RedisSerializationContext;
- import org.springframework.data.redis.serializer.StringRedisSerializer;
- @Configuration
- @EnableCaching
- public class RedisConfig {
- @Bean
- public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
- RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
- redisTemplate.setConnectionFactory(factory);
- redisTemplate.setKeySerializer(new StringRedisSerializer());
- redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
- redisTemplate.setHashKeySerializer(new StringRedisSerializer());
- redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
- return redisTemplate;
- }
- @Bean
- public RedisCacheManager redisCacheManager(RedisTemplate<String, Object> redisTemplate) { // 明确指定参数化类型
- if (redisTemplate == null || redisTemplate.getConnectionFactory() == null) {
- // 处理错误情况,例如抛出异常或采取默认行为
- throw new RuntimeException("RedisTemplate or its connection factory is null");
- }
- RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory());
- RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
- .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()));
- return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
- }
- }
复制代码
二、RedisTemplate操作-String
创建测试类演示
1.如果现在有一个登录的token想要存在redis里面
设置了有效期
- package com.qqcn;
- import org.junit.jupiter.api.Test;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.test.context.SpringBootTest;
- import org.springframework.data.redis.core.RedisTemplate;
- import java.util.UUID;
- import java.util.concurrent.TimeUnit;
- @SpringBootTest
- public class RedisTest {
- @Autowired
- private RedisTemplate<String,Object> redisTemplate;
- @Test
- public void testString(){
- String key = "user:token:0001";
- // 实际当中可以用jwt
- redisTemplate.opsForValue().set(key, UUID.randomUUID().toString(),30, TimeUnit.MINUTES);
- System.out.println(redisTemplate.opsForValue().get(key));
- }
- }
复制代码 启动redis
启动乐成
token已存入
2.如果我们要对一篇文章进行访问数的统计
每检察一次都进行计数
- @Test
- public void testString2(){
- String key = "article:A00001:viewsCount";
- redisTemplate.opsForValue().increment(key);
- System.out.println(redisTemplate.opsForValue().get(key));
- }
复制代码 第一次执行是1,第二次就是2了
3.如果想要存储一个对象
如果一个对象无需修改而是仅查询作用,那么存String也是一个很好的选择
在配置类中做了一个json的序列化处理,所以存储对象进去最终会被转换成json字符串
- @Test
- public void testString3(){
- Map<String,Object> user = new HashMap<>();
- user.put("id","0001");
- user.put("name","张三丰");
- user.put("age",28);
- String key = "user:0001";
- redisTemplate.opsForValue().set(key,user);
- System.out.println(redisTemplate.opsForValue().get(key));
- }
复制代码 可以看到被存储为json格式
三、RedisTemplate操作-Hash
好比说在做一个电商类项目的时间,会有一个购物车对象
Hash是kv键值类型,值类型类似map结构,更得当用来生存对象
如果用opsForHash().put(Object key, Object hashkey, Object value)此中hashkey表示右边的k,value表示右边的v,如果一个一个存比力繁琐
所以我们这里用opsForHash().putAll(Object key,Map m)
测试取出购物车有哪些商品
- @Test
- public void testHash(){
- String key = "user:0001:cart";
- Map<String, Object> shoppingCart = new HashMap<>();
- shoppingCart.put("cartId", "123456789");
- shoppingCart.put("userId", "987654321");
- List<Map<String, Object>> items = List.of(
- Map.of("itemId", "1", "itemName", "手机", "price", 999.99, "quantity", 1),
- Map.of("itemId", "2", "itemName", "笔记本电脑", "price", 1499.99, "quantity",
- 2),
- Map.of("itemId", "3", "itemName", "耳机", "price", 49.99, "quantity", 3)
- );
- shoppingCart.put("items", items);
- shoppingCart.put("totalAmount", 3149.92);
- shoppingCart.put("creationTime", "2046-03-07T10:00:00");
- shoppingCart.put("lastUpdateTime", "2046-03-07T12:30:00");
- shoppingCart.put("status", "未结账");
- redisTemplate.opsForHash().putAll(key, shoppingCart);
- System.out.println(redisTemplate.opsForHash().get(key, "items"));
- }
复制代码
四、RedisTemplate操作-Set
是数据库中一种无序的、不重复的数据结构,用于存储一组唯一的元素
好比想去存储某个用户的粉丝有哪些人
redis中统计黑白常的快的
添加了三个粉丝add进去
- @Test
- public void testSet(){
- String key = "author:0001:fans";
- redisTemplate.opsForSet().add(key,"张三","李四","王五");
- System.out.println("粉丝量:" + redisTemplate.opsForSet().size(key));
- }
复制代码 结果为粉丝量为3
五、RedisTemplate操作-ZSet
是一个有序且唯一的集合,每个元素都与一个浮点数分数干系联,使得集合中的元素可以根据分数进行排序,默认按照分数进行升序排序。
若要降序查询:
redisTemplate.opsForZSet().reverseRange(key,0,-1)
添加成员的时间有一个分数值,通过分数值进行排序处理
好比我们想存储某个用户的挚友列表
除了添加用户的信息,还可以添加时间,可以根据时间获取挚友的列表
- @Test
- public void testZSet(){
- String key = "user:0001:friends";
- //取添加好友的时间的毫秒值
- redisTemplate.opsForZSet().add(key,"张三",System.currentTimeMillis());
- redisTemplate.opsForZSet().add(key,"李四",System.currentTimeMillis());
- redisTemplate.opsForZSet().add(key,"王五",System.currentTimeMillis());
- //倒序
- Set<Object> set = redisTemplate.opsForZSet().reverseRange(key, 0, -1);
- System.out.println(set);
- }
复制代码 最后存入的在最前面(按时间的降序来处理)
六、RedisTemplate操作-List
一种简单的字符串列表,按照插入顺序排序
在redis中可以把List想象成通道,两边都是开放的,可以左进右出,也可以右进左出,或者左进左出也可以
可以用List实现队列的功能,好比现有一个订单的哀求处理,在买商品的时间很大概因为并发的题目引发别的题目,可以利用队列让订单做一个排队
存入数据:
redisTemplate.opsForList().leftPush(key,order1);
获取并移除数据:
redisTemplate.opsForList().rightPop(key)
- @Test
- public void testList(){
- String key = "order:queue";
- //订单1
- Map<String, Object> order1 = new HashMap<>();
- order1.put("orderId", "1001");
- order1.put("userId", "2001");
- order1.put("status", "已完成");
- order1.put("amount", 500.75);
- order1.put("creationTime", "2024-08-09T09:30:00");
- order1.put("lastUpdateTime", "2024-08-9T10:45:00");
- order1.put("paymentMethod", "在线支付");
- order1.put("shoppingMethod", "自提");
- order1.put("remarks", "尽快处理");
- //订单2
- Map<String, Object> order2 = new HashMap<>();
- order2.put("orderId", "1002");
- order2.put("userId", "2002");
- order2.put("status", "待处理");
- order2.put("amount", 280.99);
- order2.put("creationTime", "2024-08-09T11:00:00");
- order2.put("lastUpdateTime", "2024-08-09T11:00:00");
- order2.put("paymentMethod", "货到付款");
- order2.put("shoppingMethod", "快递配送");
- order2.put("remarks", "注意保鲜");
- // A程序接收订单请求并将其加入队列
- redisTemplate.opsForList().leftPush(key,order1);
- redisTemplate.opsForList().leftPush(key,order2);
- // B程序从订单队列中获取订单数据并处理
- System.out.println("处理订单:" + redisTemplate.opsForList().rightPop(key));
- }
复制代码 从右边取出的数据:订单1
还剩订单2
七、@RedisHash注解
除了用RedisTemplate来操作hash数据,还提供了@RedisHash注解
用于将java对象直接映射到redis的hash数据当中
java对象的存储和检索变得更加简单
关键步调:
1.@RedisHash注解标注在实体类上
2.创建接口并继续CrudRepository
1.创建一个实体类
@Id表示id为主键
- package com.qqcn.entity;
- import lombok.Data;
- import org.springframework.data.annotation.Id;
- import org.springframework.data.redis.core.RedisHash;
- @RedisHash
- @Data
- public class user {
- @Id
- private Integer id;
- private String name;
- private Integer age;
- private String phone;
- }
复制代码
2.创建一个接口
想要用此注解还必要创建一个接口
继续CrudRepository(增删改查)设定泛型第一个是操作的实体,第二个是主键的类型
定义完了接口之后就具备了对对象的基于redis的增删改查的本事
- package com.qqcn.repository;
- import com.qqcn.entity.User;
- import org.springframework.data.repository.CrudRepository;
- public interface UserRedisRepository extends CrudRepository<User,Integer> {
- }
复制代码
3.测试类进行测试
注入UserRedisRepository,进行测试类测试
- @Autowired
- private UserRedisRepository userRedisRepository;
- @Test
- public void testRedisHash(){
- User user = new User();
- user.setId(100);
- user.setName("张三丰");
- user.setAge(18);
- user.setPhone("18899998888");
- //保存到redis里面
- userRedisRepository.save(user);
- //读取
- Optional<User> redisUser = userRedisRepository.findById(100);
- System.out.println(redisUser);
- //更新
- user.setPhone("18899998866");
- //看数据是否存在,存在就修改,不存在就保存
- userRedisRepository.save(user);
- //再打印一次
- Optional<User> redisUser2 = userRedisRepository.findById(100);
- System.out.println(redisUser2);
- //删除
- //userRedisRepository.deleteById(100);
- //查看id是否存在
- boolean exists = userRedisRepository.existsById(100);
- System.out.println(exists);
- }
复制代码
测试结果
因为没有指定key,key默认是 路径:id
八、缓存管理注解
redis最常见的用途就是用作缓存
而springboot的缓存管理功能旨在帮助开发人员轻松地在应用步伐中利用缓存,以进步性能和响应速度,它提供了一套注解和配置,使得开发人员可以在方法级别上进行缓存控制,并且支持多种缓存存储提供步伐
@Cacheable一样平常被用于查询方法,先在redis看看有无数据,如果有就直接在缓存里面拿,如果没有就查询数据库,并且会将查询到的数据放入缓存
@CachePut主要被用于更新缓存,一样平常大概用在新增和修改的方法上面
@CacheEvict主要用于扫除缓存,一样平常会用在删除方法的上面
1.预备表
- CREATE TABLE product (
- id INT AUTO_INCREMENT PRIMARY KEY,
- name VARCHAR(255) NOT NULL,
- description TEXT,
- price DECIMAL(10, 2) NOT NULL,
- stock INT NOT NULL
- );
复制代码- INSERT INTO product (name, description, price, stock) VALUES
- ('iPhone 15', '最新的iPhone型号', 8999.99, 100),
- ('三星Galaxy S24', '旗舰安卓手机', 7899.99, 150),
- ('MacBook Pro', '专业人士的强大笔记本电脑', 15999.99, 50),
- ('iPad Air', '性能强劲的便携式平板电脑', 5599.99, 200),
- ('索尼PlayStation 6', '下一代游戏机', 4499.99, 75);
复制代码
2.根据表创建实体类
- package com.qqcn.entity;
- import com.baomidou.mybatisplus.annotation.TableId;
- import com.baomidou.mybatisplus.annotation.TableName;
- import lombok.Data;
- @Data
- @TableName
- public class Product {
- @TableId
- private Integer id;
- private String name;
- private String description;
- private Double price;
- private Integer stock;
- }
复制代码
3.创建一个mapper接口
到数据库当中查数据,必须要有数据库操作的存在
前面创建项目的时间是添加了mybatis-plus的依赖
所以创建一个mapper来简化数据库的操作
在mybatis-plus里面想要实现增删改查必要在接口上面继续BaseMapper,泛型里面指定实体是谁
- package com.qqcn.mapper;
- import com.baomidou.mybatisplus.core.mapper.BaseMapper;
- import com.qqcn.entity.Product;
- public interface ProductMapper extends BaseMapper<Product> {
- }
复制代码 注意:要在启动类上加一个注解@MapperScan扫描一下
4.创建一个service
在此创建增删改查的几个方法
- package com.qqcn.service;
- import com.qqcn.entity.Product;
- public interface ProductService {
- //查询
- public Product getProductById(Integer id);
- //新增 还是要写一个返回值
- //因为我们要用注解@CachePut缓存数据是只能缓存方法的返回值
- public Product addProduct(Product product);
- //修改
- public Product updateProduct(Product product);
- //删除
- public void deleteProductById(Integer id);
- }
复制代码
5.创建相应的实现类
- package com.qqcn.service.impl;
- import com.qqcn.entity.Product;
- import com.qqcn.mapper.ProductMapper;
- import com.qqcn.service.ProductService;
- import jakarta.annotation.Resource;
- import org.springframework.stereotype.Service;
- @Service
- public class ProductServiceImpl implements ProductService {
- @Resource
- private ProductMapper productMapper;
- @Override
- public Product getProductById(Integer id) {
- return productMapper.selectById(id);
- }
- @Override
- public Product addProduct(Product product) {
- productMapper.insert(product);
- return product;
- }
- @Override
- public Product updateProduct(Product product) {
- productMapper.updateById(product);
- return product;
- }
- @Override
- public void deleteProductById(Integer id) {
- productMapper.deleteById(id);
- }
- }
复制代码
6.添加缓存存储的注解
我们希望看到的是
首先查找是先从redis里面找数据,如果有的话sql就不执行,如果sql执行了的话,日记里面会体现
value指定缓存的名称,key是生存在redis中的键(必要拼接id用单引号)
设置之后增删改查的同时会作缓存同步的处理,作高频访问的时间,可以进步效率,同时降低mysql压力
- package com.qqcn.service.impl;
- import com.qqcn.entity.Product;
- import com.qqcn.mapper.ProductMapper;
- import com.qqcn.service.ProductService;
- import jakarta.annotation.Resource;
- import org.springframework.cache.annotation.CacheEvict;
- import org.springframework.cache.annotation.CachePut;
- import org.springframework.cache.annotation.Cacheable;
- import org.springframework.stereotype.Service;
- @Service
- public class ProductServiceImpl implements ProductService {
- @Resource
- private ProductMapper productMapper;
- @Cacheable(value = "product", key = "'product:' + #id")
- @Override
- public Product getProductById(Integer id) {
- return productMapper.selectById(id);
- }
- @CachePut(value = "product", key = "'product:'+ #product.id")
- @Override
- public Product addProduct(Product product) {
- productMapper.insert(product);
- return product;
- }
- @CachePut(value = "product", key = "'product:'+ #product.id")
- @Override
- public Product updateProduct(Product product) {
- productMapper.updateById(product);
- return product;
- }
- @CacheEvict(value = "product", key = "'product:' + #id")
- @Override
- public void deleteProductById(Integer id) {
- productMapper.deleteById(id);
- }
- }
复制代码
7.测试类进行测试
(1)测试查询
- @Autowired
- private ProductService productService;
- @Test
- public void testQuery(){
- Product product = productService.getProductById(1);
- System.out.println(product);
- }
复制代码 这里在执行时发生了一个错误java.sql.SQLException: Access denied for user ‘root‘@‘localhost‘
堕落在于mysql的密码是数字同时用的是.yml文件 所以我们必要把密码用双引号引起来
- spring:
- data:
- redis:
- port: 6379
- host: localhost
- datasource:
- username: root
- password: "*****"
- url: jdbc:mysql://localhost:3306/testdb
复制代码
更改之后测试乐成,测试结果如下
redis中已缓存
重新测第二次
没有sql语句,因为我们可以在缓存中读取了
(2)测试更新(和新增同理,这里只演示更新)
- @Test
- public void testUpdate(){
- //将刚刚查询到的名字修改一下
- Product product = productService.getProductById(1);
- product.setName("苹果15");
- //这里更新表一定会有sql日志,更新了缓存
- productService.updateProduct(product);
- //修改之后再一次查询,这里就没有sql日志了
- Product product2 = productService.getProductById(1);
- System.out.println(product2);
- }
复制代码 测试结果,只有中间更新的时间有sql日记,查询走的都是缓存
redis已同步更改
(3)测试删除
删完的同时,缓存中的数据也会不存在
- @Test
- public void testDelete(){
- productService.deleteProductById(1);
- }
复制代码 测试结果,会打印一个删除的sql日记
mysql数据表中该数据被删除
redis中的缓存也没有了
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |