前言
本次我们来一起学习Springboot的缓存,主要是默认缓存的体验以及Redis的使用
提示:以下是本篇文章正文内容,下面案例可供参考
一、Springboot缓存管理的作用
缓存是分布式系统中的重要组件,主要解决数据库数据的高并发访问。在实际开发中,尤其是用户访问量较大的网站,用户对高频热点数据的访问非常频繁,为了提高服务器访问性能、减少数据库的压力、提高用户体验,使用缓存显得尤为重要。
二、基础环境的搭建
1.创建环境
分别引入JPA,Web,MySQL以及待会会用到的Redis()先别导入,先试用一下默认的缓存组件 依赖启动器。
2.数据库创建两张表(t_article和t_comment),并创建对应的实体类
在数据库中创建对应的库(这里我用的是之前的库Springboottest1),并生成两张表。
3.在IDEA中创建实体类domain
//该实体类与哪张表互相映射
@Entity(name = "t_comment")
public class Comment {
@Id//表示映射对应的主键
//表示主键生成策略(自动增长)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String content;
private String author;
//指定映射的表字段名(表中的名字就是注解的name)
@Column(name = "a_id")
private Integer aId;
(getset方法略)
4.创建实体类的接口
repository(接口)对JPA语句进行拓展修改(导包别导错)
import com.gdcp.domain.Comment;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import javax.transaction.Transactional;
public interface CommentRepository extends JpaRepository<Comment,Integer> {
//根据评论id修改评论作者评论作者author
@Transactional//进行数控
@Modifying//对数据库进行变更必须加
@Query("UPDATE t_comment c SET c.author=?1 where c.id=?2")
public int updateComment(String author,Integer id);
}
5.service(业务操作类)在该类中编写数据的查询、修改和删除操作
@Service
public class CommentService {
@Autowired
private CommentRepository commentRepository;
/*
* 查询,根据id查询
* */
public Comment findById(Integer id){
Optional<Comment>byId=commentRepository.findById(id);
if(byId.isPresent()){
return byId.get();
}
return null;
}
/*
* 更新
* */
public int updateComment(Comment comment){
int i = commentRepository.updateComment(comment.getAuthor(), comment.getId());
return i;
}
/*
* 删除
* */
public void deleteComment(Integer id){
commentRepository.deleteById(id);
}
}
6.创建控制类
编写Web访问层Controller文件CommentController,使用注入的CommentService实例对象编写对Comment评论数据的查询、修改和删除方法
@RestController
public class CommentController {
@Autowired
private CommentService commentService;
/*
* 查询
* */
@GetMapping("/get/{id}")
//@@PathVariable("id")取得id赋值到getmapping中去
public Comment findById(@PathVariable("id") Integer id){
Comment comment=commentService.findById(id);
return comment;
}
/*
*更新
* */
@GetMapping("/update/{id}/{author}")
public int updateComment(@PathVariable("id") int id,@PathVariable("author") String author){
Comment comment = commentService.findById(id);
comment.setAuthor(author);
int i = commentService.updateComment(comment);
return i;
}
/*
*删除
* */
@GetMapping("/delete/{id}")
public void deleteById(@PathVariable("id")Integer id){
commentService.deleteComment(id);
}
}
7.编写全局配置文件连接数据库
spring.datasource.url=jdbc:mysql://localhost:3306/springboottest1?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=自己的数据库密码
#显示使用JPA进行数据库查询的sql语句
spring.jpa.show-sql=true
8.测试
这里会发现,如果没开启缓存,查询数据时,会重复查询,不利于服务器处理,特别是高并发情况下。
9.开启缓存
在启动类中添加缓存注解
@EnableCaching
在业务处理类(Service)中开启缓存处理
//将查询的id存放到缓存中
@Cacheable(cacheNames = "comment",unless = "#result==null")
//删除
@CacheEvict(cacheNames = "comment")
//更新
@CachePut(cacheNames = "comment",key = "#result.id")
代码如下(示例):
@Service
public class CommentService {
@Autowired
private CommentRepository commentRepository;
/*
* 查询,根据id查询
* */
//将查询的id存放到缓存中
@Cacheable(cacheNames = "comment",unless = "#result==null")
public Comment findById(Integer id){
Optional<Comment> byId=commentRepository.findById(id);
if(byId.isPresent()){
return byId.get();
}
return null;
}
/*
* 更新
* */
//更新
@CachePut(cacheNames = "comment",key = "#result.id")
public int updateComment(Comment comment){
int i = commentRepository.updateComment(comment.getAuthor(), comment.getId());
return i;
}
/*
* 删除
* */
//删除
@CacheEvict(cacheNames = "comment")
public void deleteComment(Integer id){
commentRepository.deleteById(id);
}
}
测试结果
会发现我们的数据被缓存了,多次访问仍然不会重复数据。
Spring Boot缓存注解介绍
1.@EnableCaching注解
@EnableCaching是由Spring框架提供的,Spring Boot框架对该注解进行了继承,该注解需要配置在类上(在Spring Boot中,通常配置在项目启动类上),用于开启基于注解的缓存支持。
2.@Cacheable注解
@Cacheable注解也是由Spring框架提供的,可以作用于类或方法(通常用在数据查询方法上),用于对方法结果进行缓存存储。
@Cacheable注解的执行顺序是,先进行缓存查询,如果为空则进行方法查询,并将结果进行缓存;如果缓存中有数据,不进行方法查询,而是直接使用缓存数据。
属性名 | 说明 |
---|---|
value/cacheNames | 指定缓存空间的名称,必配属性。这两个属性二选一使用 |
key | 指定缓存数据的key,默认使用方法参数值,可以使用SpEL表达式 |
keyGenerator | 指定缓存数据的key的生成器,与key属性二选一使用 |
cacheManager | 指定缓存管理器 |
cacheResolver | 指定缓存解析器,与cacheManager属性二选一使用 |
condition | 指定在符合某条件下,进行数据缓存 |
unless | 指定在符合某条件下,不进行数据缓存 |
sync | 指定是否使用异步缓存。默认false |
3.@CachePut注解
@CachePut注解是由Spring框架提供的,可以作用于类或方法(通常用在数据更新方法上),该注解的作用是更新缓存数据。@CachePut注解的执行顺序是,先进行方法调用,然后将方法结果更新到缓存中。
@CachePut注解也提供了多个属性,这些属性与@Cacheable注解的属性完全相同。
4.@CacheEvict注解
@CacheEvict注解是由Spring框架提供的,可以作用于类或方法(通常用在数据删除方法上),该注解的作用是删除缓存数据。@CacheEvict注解的默认执行顺序是,先进行方法调用,然后将缓存进行清除。
@CacheEvict注解也提供了多个属性,这些属性与@Cacheable注解的属性基本相同,除此之外,还额外提供了两个特殊属性allEntries和beforeInvocation。
(1)allEntries属性
allEntries属性表示是否清除指定缓存空间中的所有缓存数据,默认值为false(即默认只删除指定key对应的缓存数据)。
(2)beforeInvocation属性
beforeInvocation属性表示是否在方法执行之前进行缓存清除,默认值为false(即默认在执行方法后再进行缓存清除)。
5.@Caching注解
@Caching注解用于针对复杂规则的数据缓存管理,可以作用于类或方法,在@Caching注解内部包含有Cacheable、put和evict三个属性,分别对应于@Cacheable、@CachePut和@CacheEvict三个注解。
@Caching(cacheable={@Cacheable(cacheNames ="comment",key = "#id")},
put = {@CachePut(cacheNames = "comment",key = "#result.author")})
public Comment getComment(int comment_id){
return commentRepository.findById(comment_id).get();
}
6.@CacheConfig注解
@CacheConfig注解使用在类上,主要用于统筹管理类中所有使用@Cacheable、@CachePut和@CacheEvict注解标注方法中的公共属性,这些公共属性包括有cacheNames、keyGenerator、cacheManager和cacheResolver。
@CacheConfig(cacheNames = "comment")
@Service
public class CommentService {
@Autowired
private CommentRepository commentRepository;
@Cacheable
public Comment findById(int comment_id){
Comment comment = commentRepository.findById(comment_id).get();
return comment; }...}
基于注解的Redis缓存实现
Spring Boot支持的缓存组件
Spring Boot支持的缓存组件有:
(1)Generic
(2)JCache (JSR-107) (EhCache 3、Hazelcast、Infinispan等)
(3)EhCache 2.x
(4)Hazelcast
(5)Infinispan
(6)Couchbase
(7)Redis
(8)Caffeine
(9)Simple(默认)
操作例子
1.添加Spring Data Redis依赖启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.Redis服务连接配置
#Redis服务地址
spring.redis.host=127.0.0.1
#redis服务器连接端口
spring.redis.port=6379
#redis服务器连接密码(默认值为空)
spring.redis.password=
3.使用@Cacheable、@CachePut、@CacheEvict注解定制缓存管理
@Service
@Transactional
public class CommentService {
@Autowired
private CommentRepository commentRepository;
/*
* 查询,根据id查询
* */
//将查询的id存放到缓存中
@Cacheable(cacheNames = "comment",unless = "#result==null")
public Comment findById(int comment_id){
Optional<Comment> optional = commentRepository.findById(comment_id);
if(optional.isPresent()){
return optional.get();
}
return null;
}
/*
* 更新
* */
@CachePut(cacheNames = "comment",key = "#result.id")
public Comment updateComment(Comment comment){
int i = commentRepository.updateComment(comment.getAuthor(), comment.getId());
Optional<Comment> byId = commentRepository.findById(comment.getId());
if(byId.isPresent()){
return byId.get();
}
return null;
}
/*
* 删除
* */
@CacheEvict(cacheNames = "comment")
public void deleteComment(Integer id){
commentRepository.deleteById(id);
}
}
4.基于注解的Redis查询缓存测试
@SpringBootApplication
//开启默认缓存注解
@EnableCaching
5.将缓存对象实现序列化
import javax.persistence.*;
import java.io.Serializable;
//该实体类与哪张表互相映射
@Entity(name = "t_comment")
//实现序列化接口
public class Comment implements Serializable {
@Id//表示映射对应的主键
//表示主键生成策略(自动增长)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String content;
private String author;
//指定映射的表字段名(表中的名字就是注解的name)
@Column(name = "a_id")
private Integer aId;
}
6.基于注解的Redis缓存查询测试
查询结果为一条,反复查询依然调用Redis缓存空间的
7.基于注解的Redis缓存更新测试
8.基于注解的Redis缓存删除测试
基于API的Redis缓存实现
① **使用Redis API **进行业务数据缓存管理
编写一个进行业务处理的类ApiCommentService,使用@Autowired注解注入Redis API中常用的RedisTemplate(类似于Java基础API中的JdbcTemplate);然后在数据查询、修改和删除三个方法中,根据业务需求分别进行数据缓存查询、缓存存储、缓存更新和缓存删除。同时,Comment数据对应缓存管理的key值都手动设置了一个前缀“comment_”,这是针对不同业务数据进行缓存管理设置的唯一key,避免与其他业务缓存数据的key重复。
@Service
@Transactional
public class ApiCommentService {
@Autowired
private CommentRepository commentRepository;
@Autowired
private RedisTemplate redisTemplate;
/*
* 查询方法
* */
public Comment findById(Integer id){
Object o = redisTemplate.opsForValue().get("comment_" + id);
if (o!=null){
//缓存有数据
return (Comment)o;
}else {
Optional<Comment> byId = commentRepository.findById(id);
if(byId.isPresent()){
Comment comment = byId.get();
redisTemplate.opsForValue().set("comment_"+id,comment,1, TimeUnit.DAYS);
return comment;
}
return null;
}
}
/*
* 更新方法
* */
public Comment updateComment(Comment comment){
commentRepository.updateComment(comment.getAuthor(), comment.getaId());
redisTemplate.opsForValue().set("comment_"+comment.getId(),comment);
return comment;
}
/*
* 删除方法
* */
public void deleteComment(int comment_id){
commentRepository.deleteById(comment_id);
redisTemplate.delete("comment_"+comment_id);
}
}
② 编写Web访问层Controller文件ApiCommentController
在类上加入了@RequestMapping(“/api”)注解用于窄化请求,并通过@Autowired注解注入了新编写的ApiCommentService实例对象,然后调用ApiCommentService中的相关方法进行数据查询、修改和删除。
@RestController
@RequestMapping("Api")
public class ApiCommentController {
@Autowired
private CommentService commentService;
@Autowired
private ApiCommentService apiCommentService;
/*
* 查询
* */
@GetMapping("/get/{id}")
//@@PathVariable("id")取得id赋值到getmapping中去
public Comment findById(@PathVariable("id") Integer id){
Comment comment=apiCommentService.findById(id);
return comment;
}
/*
*更新
* */
@GetMapping("/update/{id}/{author}")
public Comment updateComment(@PathVariable("id") int id,@PathVariable("author") String author){
Comment comment = apiCommentService.findById(id);
comment.setAuthor(author);
Comment comment1 = apiCommentService.updateComment(comment);
return comment1;
}
/*
*删除
* */
@GetMapping("/delete/{id}")
public void deleteById(@PathVariable("id")Integer id){
apiCommentService.deleteComment(id);
}
}
③基于API的Redis缓存实现的相关配置
1.基于API的Redis缓存实现不需要@EnableCaching注解开启基于注解的缓存支持。
2.基于API的Redis缓存实现需要在Spring Boot项目的pom.xml文件中引入Redis依赖启动器,并在配置文件中进行Redis服务连接配置,同时将进行数据存储的Comment实体类实现序列化接口。
缓存测试与基于注解的Redis缓存实现的测试完全一样。