Spring Boot demo系列(十):Redis缓存

1 概述

本文演示了如何在Spring Boot中将Redis作为缓存使用,具体的内容包括:

  • 环境搭建
  • 项目搭建
  • 测试

2 环境

  • Redis
  • MySQL
  • MyBatis Plus

3 Redis安装

Redis安装非常简单,以笔者的Manjaro为例,直接paru安装:

paru -S redis

UbuntuCentOS之类的都提供了软件包安装:

1sudo apt install redis 2sudo yum install redis

如果想从源码编译安装:

1wget http://download.redis.io/redis-stable.tar.gz 2tar xvzf redis-stable.tar.gz 3cd redis-stable 4make

Windows以及其他系统的安装可以参考此处

4 新建项目

新建项目,加入如下依赖:

Maven

1<dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-data-redis</artifactId> 4</dependency> 5<dependency> 6 <groupId>mysql</groupId> 7 <artifactId>mysql-connector-java</artifactId> 8 <scope>runtime</scope> 9</dependency> 10<dependency> 11 <groupId>org.projectlombok</groupId> 12 <artifactId>lombok</artifactId> 13 <optional>true</optional> 14</dependency> 15<dependency> 16 <groupId>com.baomidou</groupId> 17 <artifactId>mybatis-plus-boot-starter</artifactId> 18 <version>3.4.2</version> 19</dependency>

Gradle

1implementation("com.baomidou:mybatis-plus-boot-starter:3.4.2") 2implementation("mysql:mysql-connector-java:8.0.23")

项目结构:

在这里插入图片描述

5 配置类

MyBatis Plus+Redis配置类:

1@Configuration 2@MapperScan("com.example.demo.dao") 3public class MyBatisPlusConfig { 4} 5 6 7@Configuration 8@AutoConfigureAfter(RedisAutoConfiguration.class) 9@EnableCaching 10public class RedisConfig { 11 @Bean 12 public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) { 13 RedisTemplate<String, Object> template = new RedisTemplate<>(); 14 template.setKeySerializer(new StringRedisSerializer()); 15 template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); 16 template.setConnectionFactory(factory); 17 return template; 18 } 19 20 @Bean 21 public CacheManager cacheManager(RedisConnectionFactory factory) { 22 RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig() 23 .serializeKeysWith( 24 RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()) 25 ).serializeValuesWith( 26 RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()) 27 ); 28 return RedisCacheManager.builder(factory).cacheDefaults(configuration).build(); 29 } 30}

重点说一下Redis配置类,这个类主要生成两个Bean

  • RedisTemplate:简化Redis操作的数据访问类
  • CacheManagerSpring的中央缓存管理器

其中RedisTemplate是一个模板类,第一个参数的类型是该template使用的键的类型,通常是String,第二个参数的类型是该template使用的值的类型,通常为ObjectSeriazable

setKeySerializersetValueSerializer分别设置键值的序列化器。键一般为String类型,可以使用自带的StringRedisSerializer。对于值,可以使用自带的GenericJackson2RedisSerializer

CacheManager的配置类似,就不重新说了。

6 实体类

1@Getter 2@Setter 3@ToString 4@AllArgsConstructor 5@NoArgsConstructor 6public class User { 7 private Integer id; 8 private String name; 9}

7 持久层

1public interface UserMapper extends BaseMapper<User> { 2}

8 业务层

1@org.springframework.stereotype.Service 2@Transactional 3@RequiredArgsConstructor(onConstructor = @__(@Autowired)) 4public class Service { 5 private final UserMapper mapper; 6 7 @CachePut(value = "user",key = "#user.id") 8 public User save(User user){ 9 User oldUser = mapper.selectById(user.getId()); 10 if(oldUser == null){ 11 mapper.insert(user); 12 return user; 13 } 14 if(mapper.updateById(user) == 1) 15 return user; 16 return oldUser; 17 } 18 19 @CacheEvict(value = "user",key = "#id") 20 public boolean delete(Integer id){ 21 return mapper.deleteById(id) == 1; 22 } 23 24 @Cacheable(value = "user",key = "#id") 25 public User select(Integer id){ 26 return mapper.selectById(id); 27 } 28 29 @Cacheable(value="allUser",key = "#root.target+#root.methodName") 30 //root.target是目标类,这里是com.example.demo.Service,root.methodName是方法名,这里是selectAll 31 public List<User> selectAll(){ 32 return mapper.selectList(null); 33 } 34}

注解说明如下:

  • @CachePut:执行方法体再将返回值缓存,一般用于更新数据
  • @CacheEvict:删除缓存,一般用于删除数据
  • @Cacheable:查询缓存,如果有缓存就直接返回,没有缓存的话执行方法体并将返回值存入缓存,一般用于查询数据

三个注解都涉及到了key以及value属性,实际上,真正的存入Rediskey是两者的组合,比如:

@Cacheable(value="user",key="#id")

则存入的Redis中的key为:

在这里插入图片描述

而存入对应的值为方法返回值序列化后的结果,比如如果返回值为User,则会被序列化为:

在这里插入图片描述

9 配置文件

1spring: 2 datasource: 3 url: jdbc:mysql://localhost:3306/test 4 username: root 5 password: 123456 6 redis: 7 database: 0 8 host: 127.0.0.1 9 port: 6379 10logging: 11 level: 12 com.example.demo: debug

spring.redis.database指定数据库的索引,默认为0,hostport分别指定主机(默认本地)以及端口(默认6379)。

也就是说,简单配置的话可以完全省略Redis相关配置,仅指定数据库连接url、用户名以及密码:

1spring: 2 datasource: 3 url: jdbc:mysql://localhost:3306/test 4 username: root 5 password: 123456 6logging: 7 level: 8 com.example.demo: debug

10 启动Redis

10.1 启动Redis服务器

Redis服务器启动需要一个配置文件,默认位置为/etc/redis.conf(源码编译安装的话在源文件夹内),建议先复制一份:

cp /etc/redis.conf ~/Desktop/

默认的配置文件为单机Redis配置,端口6379redis-server可以直接运行:

sudo redis-server redis.conf

10.2 连接服务器

连接可以通过自带的redis-cli命令:

redis-cli -h localhost -p 6379

默认情况下可以直接使用

redis-cli

连接。

基本操作:

  • keys *:查询所有键
  • get key:查询key所对应的值
  • flushall:清空所有键

在这里插入图片描述

11 测试

1@SpringBootTest 2@RequiredArgsConstructor(onConstructor = @__(@Autowired)) 3class DemoApplicationTests { 4 5 private final Service service; 6 7 @Test 8 void select() { 9 service.select(1); 10 service.select(1); 11 } 12 13 @Test 14 void selectAll(){ 15 service.selectAll(); 16 service.selectAll(); 17 } 18 19 @Test 20 void delete(){ 21 service.delete(1); 22 } 23 24 @Test 25 void save(){ 26 User user = new User(1,"name1"); 27 service.save(user); 28 service.select(user.getId()); 29 user.setName("name2"); 30 service.save(user); 31 service.select(user.getId()); 32 } 33}

执行其中的select,会发现MyBatis Plus只有一次select的输出,证明缓存生效了:

在这里插入图片描述

而把缓存注解去掉后,会有两次select输出:

在这里插入图片描述

其它测试方法就不截图了,原理类似。

12 附录:Kotlin中的一些细节

12.1 String数组

其实@Cacheable/@CacheEvict/@CachePut中的value都是String [],在Java中可以直接写上value,在Kotlin中需要[value]

12.2 @class

序列化到Redis时,实体类会被加上一个@class字段:

在这里插入图片描述

这个标识供Jackson反序列化时使用,笔者一开始的实体类实现是:

data class User(var id:Int?=null, var name:String="")

但是序列化后不携带@class字段:

在这里插入图片描述

在反序列化时直接报错:

1Could not read JSON: Missing type id when trying to resolve subtype of [simple type, class java.lang.Object]: missing type id property '@class' 2 at [Source: (byte[])"{"id":1,"name":"name2"}"; line: 1, column: 23]; nested exception is com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class java.lang.Object]: missing type id property '@class' 3 at [Source: (byte[])"{"id":1,"name":"name2"}"; line: 1, column: 23]

解决方法有两个:

  • 手动添加@class字段
  • 将实体类设为open

12.2.1 手动添加@class

准确来说并不是手动添加,而是让注解添加,需要添加一个类注解@JsonTypeInfo

1@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 2data class User(var id:Int?=null, var name:String="")

该注解的use用于指定类型标识码,该值只能为JsonTypeInfo.Id.CLASS

12.2.2 将实体类设置为open

Java中,实体类没有任何额外配置,Redis序列化/反序列化一样没有问题,是因为值序列化器GenericJackson2JsonRedisSerializer,该类会自动添加一个@class字段,因此不会出现上面的问题。

但是在Kotlin中,类默认不是open的,也就是无法添加@class字段,因此便会反序列化失败,解决方案是将实体类设置为open

open class User(var id:Int?=null, var name:String="")

但是缺点是不能使用data class了。

13 参考源码

Java版:

Kotlin版:

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )