基于注解的Redis缓存实现
下面,在第一小节Spring Boot默认缓存管理的基础上引入Redis缓存组件,使用基于注解的方式讲解Spring Boot整合Redis缓存的具体实现。
(1)添加Spring Data Redis依赖启动器。在chapter06项目的pom.xml文件中添加Spring Data Redis依赖启动器,示例代码如下。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
(2)Redis服务连接配置。使用类似Redis第三方缓存组件进行缓存管理时,缓存数据并不是像Spring Boot默认缓存管理那样存储在内存中,而是需要预先搭建类似Redis服务的数据仓库进行缓存存储。所以,这里首先需要安装并开启Redis服务;然后在项目的全局配置文件application.properties中添加Redis服务的连接配置,示例代码如下。
# Redis服务地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
(3)使用@Cacheable、@CachePut、@CacheEvict注解定制缓存管理。对CommentService类中的方法进行修改使用@Cacheable、@CachePut、@CacheEvict三个注解定制缓存管理,分别进行缓存存储、缓存更新和缓存删除的演示,修改后的内容如文件1所示。
文件1 CommentService.java
1 import com.itheima.domain.Comment;
2 import com.itheima.repository.CommentRepository;
3 import org.springframework.beans.factory.annotation.Autowired;
4 import org.springframework.cache.annotation.*;
5 import org.springframework.stereotype.Service;
6 import java.util.Optional;
7 @Service
8 public class CommentService {
9 @Autowired
10 private CommentRepository commentRepository;
11 @Cacheable(cacheNames = "comment",unless = "#result==null")
12 public Comment findById(int comment_id){
13 Optional<Comment> optional = commentRepository.findById(comment_id);
14 if(optional.isPresent()){
15 return optional.get();
16 }
17 return null;
18 }
19 @CachePut(cacheNames = "comment",key = "#result.id")
20 public Comment updateComment(Comment comment){
21 commentRepository.updateComment(comment.getAuthor(), comment.getaId());
22 return comment;
23 }
24 @CacheEvict(cacheNames = "comment")
25 public void deleteComment(int comment_id){
26 commentRepository.deleteById(comment_id);
27 }
28 }
文件1中,使用@Cacheable、@CachePut、@CacheEvict注解在数据查询、更新和删除方法上进行了缓存管理。其中,查询缓存@Cacheable注解中没有标记key值,将会使用默认参数值comment_id作为key进行数据保存,在进行缓存更新时必须使用同样的key;同时在查询缓存@Cacheable注解中,定义了“unless = "#result==null"”表示查询结果为空不进行缓存。
(4)基于注解的Redis查询缓存测试。通过前面的操作,已经在项目中添加了Redis缓存的依赖和Redis服务的连接配置,并且项目中已经使用@EnableCaching开启了基于注解的缓存管理,下面可以直接启动项目进行缓存测试。
启动chapter06项目,项目启动成功后,通过浏览器访问“http://localhost:8080/get/1
”查询id为1的用户评论信息(务必确保连接的Redis服务已开启),会发现浏览器数据响应错误,同时控制台出现异常信息,效果如图1所示。
图1 Redis缓存管理异常信息
从图1可以看出,查询用户评论信息Comment时执行了相应的SQL语句,但是在进行缓存存储时出现了IllegalArgumentException非法参数异常,提示信息要求对应Comment实体类必须实现序列化(“DefaultSerializer requires a Serializable payload but received an object of type”)。
(5)将缓存对象实现序列化。通过前面的异常错误提示发现,在对实体类对象进行缓存存储时必须先实现序列化(一些基本数据类型不需要序列化,因为内部已经默认实现了序列化接口),否则会查询缓存异常,导致程序无法正常执行。下面,将进行缓存存储的Comment类进行改进,实现JDK自带的序列化接口Serializable,修改后的Comment类如文件2所示。
文件2 Comment.java
1 import javax.persistence.*;
2 import java.io.Serializable;
3 @Entity(name = "t_comment") // 设置ORM实体类,并指定映射的表名
4 public class Comment implements Serializable {
5 @Id // 表明映射对应的主键id
6 @GeneratedValue(strategy = GenerationType.IDENTITY) // 设置主键自增策略
7 private Integer id;
8 private String content;
9 private String author;
10 @Column(name = "a_id") //指定映射的表字段名
11 private Integer aId;
12 // 省略属性getXX()和setXX()方法
13 // 省略toString()方法
14 }
(6)基于注解的Redis缓存查询测试。再次启动chapter06项目,项目启动成功后,通过浏览器继续访问“http://localhost:8080/get/1
”查询id为1的用户评论信息,并重复刷新浏览器查询同一条数据信息,查询结果如图2所示。
图2 findById()方法查询结果
查看控制台打印的SQL查询语句,结果如图3所示。
图3 执行findById()方法控制台显示的SQL语句
从图2和3可以看出,执行findById()方法正确查询出用户评论信息Comment,在配置了Redis缓存组件后,重复进行同样的查询操作,数据库只执行了一次SQL语句。
还可以打开Redis客户端可视化管理工具Redis Desktop Manager连接本地启用的Redis服务,查看具体的数据缓存效果,效果如图4所示。
图4 Redis数据库可视化展示
从图4可以看出,执行findById()方法查询出的用户评论信息Comment正确存储到了Redis缓存库中名为comment的名称空间下。其中缓存数据的唯一标识key值是以“名称空间comment::+参数值(comment::1)”的字符串形式体现的,而value值则是经过JDK默认序列格式化后的HEX格式存储。这种JDK默认序列格式化后的数据显然不方便缓存数据的可视化查看和管理,所以在实际开发中,通常会自定义数据的序列化格式。
(7)基于注解的Redis缓存更新测试。先通过浏览器访问“http://localhost:8080/update/1/shitou
”更新id为1的评论作者名为shitou;接着,继续访问“http://localhost:8080/get/1
”查询id为1的用户评论信息,结果如图5所示。
图5 updateComment()方法更新后的查询结果
查看控制台打印的SQL查询语句,结果如图6所示。
图6 缓存更新后findById()方法控制台显示的SQL语句
从图5和6可以看出,执行updateComment()方法更新id为1的数据时执行了一条更新SQL语句,后续调用findById()方法查询id为1的用户评论信息时没有执行查询SQL语句,且浏览器正确返回了更新后的结果,表明@CachePut缓存更新配置成功。
(8)基于注解的Redis缓存删除测试。先通过浏览器访问“http://localhost:8080/delete/1
”删除id为1的用户评论信息;接着,继续访问“http://localhost:8080/get/1
”查询id为1的用户评论信息,结果如图7所示。
图7 deleteComment()方法删除后的查询结果
可以通过Redis客户端可视化管理工具Redis Desktop Manager查看对应数据删除后的缓存信息,如图8所示(需要选择Redis仓库名springbootredis并右键单击Reload进行刷新)。
图8 Redis数据库可视化展示
从图7和8可以看出,执行deleteComment()方法删除id为1的数据后查询结果为空,之前存储在Redis数据库的数据也被删除,表明@CacheEvict缓存删除成功实现。
通过上面的案例可以看出,使用基于注解的Redis缓存实现只需要添加Redis依赖并使用几个注解可以实现对数据的缓存管理。另外,还可以在Spring Boot全局配置文件中配置Redis有效期,示例代码如下:
# 对基于注解的Redis缓存数据统一设置有效期为1分钟,单位毫秒
spring.cache.redis.time-to-live=60000
上述代码中,在Spring Boot全局配置文件中添加了“spring.cache.redis.time-to-live”属性统一配置Redis数据的有效期(单位为毫秒),这种方式相对来说不够灵活,并且这种设置方式对下一节中将要讲解的基于API的Redis缓存实现没有效果。