作者简介 :一只大皮卡丘,计算机专业学生,正在努力学习、努力敲代码中! 让我们一起继续努力学习!
该文章参考学习教材为:
《Spring Boot企业级开发教程》 黑马程序员 / 编著
文章以课本知识点 + 代码为主线,结合自己看书学习过程中的理解和感悟 ,最终成就了该文章文章用于本人学习使用 , 同时希望能帮助大家。
欢迎大家点赞👍 收藏⭐ 关注💖哦!!!(侵权可联系我,进行删除,如果雷同,纯属巧合)
在 Spring Boot 中,数据的 "管理存储"依赖于 Spring 框架中 cache ( 缓存 ) 相关的 :① org.springframework.cache.Cache 和
② org.springframework,cache.CacheManager ( 缓存管理器 )。如果程序中 没有 定义类型为 cacheManager ( 缓存管理器 ) 的 Bean 组件或者是 没有 名为 cacheResolver ( 缓存解析器 ) ,
Spring Boot 将 按照以下的顺序 来 选择并启动 缓存组件 :试 选择并启用以下缓存组件( 按照指定的顺序 ) :
① Generic
② JCache (JSR -107 ) ( EhCache 3 、Hazelcast、Infinispan等 )
③ EhCache 2.x
④ Hazelcast
(5) Infinispan
(6) Couchbase
(7) Redis
(8) Caffeine
(9) Simple ( 默认"缓存组件" , SpringBoot 默认使用该“缓存组件” 来 进行 “缓存管理” )
上面我们按照 Spring Boot 缓存组件的加载顺序 列举了 支持的9种缓存组件,在项目中 添加某个缓存管理组件 (例如 Redis) 后,Spring Boot 项目会
选择并启用对应的缓存管理器。如果项目中 同时添加了多个缓存组件,且 没有指定缓存管理器 /缓存解析器( cacheManager/cacheResolver ),那么 Spring Boot 按照 “指定的顺序” 来 选择使用 其中的一个 “缓存组件” 并进行“缓存管理” 。
在 Spring Boot默认的 “缓存管理” 项目 文章讲解的 Spring Boot默认缓存管理中,没有添加 任何缓存管理组件却能实现缓存管理。这是因为开启缓存管理后,
如果 没有指定具体的"cacheManager "或 “cacheResolver” ,SpringBoot 将按照 指定的顺序选择并使用 “缓存组件” 。如果没有任何缓存组件,会 默认使用最后一个Simple 缓存组件进行管理。Simple 缓存组件是 Spring Boot默认的缓存管理组件,它默认使用内存中的 ConcurrentHashMap 进行 缓存存储,所以在没有添加任何第三方缓存组件的情况下也可以实现内存中的缓存管理。
Spring Boot默认的 “缓存管理” 项目 的基础上,( 先创建好 默认的“缓存管理” 项目 ,再添加"Redis缓存"相关的代码 )添加
Spring Data Redis 依赖启动器 :
<dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-data-redisartifactId> dependency>
- 1
- 2
- 3
- 4
使用类似 Redis 的 缓存组件进行缓存管理时,缓存数据并不是像 Spring Boot默认缓存管理那样存储在内存中,而是需要预先搭建类似 Redis 服务的数据仓库进行缓存存储。所以,这里首先需要 安装并开启 Redis 服务 : Redis的下载、安装 和 启动 ,然后 在项目的全局配置文件 : application.properties 中添加 Redis服务的 连接配置信息 :
#Redis服务地址 spring.data.redis.host=127.0.0.1 #Redis服务器连接端口 spring.data.redis.port=6379 #Redis服务器连接密码(默认欸空) spring.data.redis.password=123456
- 1
- 2
- 3
- 4
- 5
- 6
在 CommentService类中分别使用@Cacheable、@CachePut、@CacheEvict3个注解 定制缓存管理分别演示缓存数据的存储、更新和 删除 :
CommentService.java :
@Service //加入到IOC容器中 public class CommentService { //sevice业务层 @Autowired private CommentRepository commentRepository; /** * @Cache()注解的使用 */ // #result : 表示获得 "方法" 的 "返回值" , 将查询到的数据,存储到"缓存空间"中 @Cacheable(cacheNames = "comment",key = "#comment_id",unless = "#result == null")//SPEL表达式来设置值 public Comment findById(int comment_id) { //调用commentRepository接口的祖先接口的方法来操作数据库 Optional<Comment> option = commentRepository.findById(comment_id); if (option.isPresent()) { //判断其中是否有数据 //获得其中的数据 Comment comment = option.get(); return comment; } return null; } /** * @CachePut()注解的使用 , 当更新数据库之后,同时将该方法的"返回值"来更新"缓存空间"中的数据 */ @CachePut(cacheNames = "comment",key = "#result.id") //设置该方法的"返回值"的id属性值为"key值" public Comment updateComment(int comment_id , String author) { Comment comment = findById(comment_id); comment.setAuthor(author); return commentRepository.save(comment); } /** * @CacheEvict()注解的使用 , 在删除数据库中数据成功后,同时也删除"缓存空间"中对应的”缓存数据“ */ @CacheEvict(cacheNames = "comment") public void deleteComment(int comment_id) { commentRepository.deleteById(comment_id); //调用CrudRepository接口中的方法来操作数据库 } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
上面的代码中,使用 @Cacheable、@CachePut、@CacheEvict 注解在数据查询、更新和删除方法上进行了缓存管理。在查询缓存 @Cacheable 注解中,定义了 “unless ="#result==null"”表示 “查询结果” 为空不进行缓存。
其他类的代码 :
CommentController.java :
@Controller @ResponseBody //将相应给前端的"返回值" 转换为"指定类型" 后 存入到 "响应体" 中 public class CommentController { @Autowired private CommentService commentService; /** * 根据id来查询数据 */ @GetMapping("/findById/{id}") //id为"路径变量" public Comment findById(@PathVariable("id") int comment_id) { System.out.println("123123123"); Comment comment = commentService.findById(comment_id); return comment; } /** * 更新数据 */ @GetMapping("/updateComment/{id}") public Comment updateComment(@PathVariable("id") int comment_id,@PathVariable("author") String author) { Comment comment = commentService.updateComment(comment_id, author); return comment; } /** * 删除数据 */ @GetMapping("/delete/{id}") public void deleteComment(@PathVariable("id") int comment_id) { commentService.deleteComment(comment_id); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
将"缓存对象" ( Comment对象 ) 实现 “序列化” :
Comment.java :
//指定该实现类映射的数据库表 @Entity(name = "t_comment") //设置ORM实体类, 并指定对应的表明 public class Comment implements Serializable { // 对"缓存对象"进行"序列化" //表示数据库表中主键对应的属性 @Id @GeneratedValue(strategy= GenerationType.IDENTITY) //设置主键的生成策略 (主键自增) private Integer id; @Column(name = "content") //指定映射的表字段名 private String content; @Column(name = "author") private String author; @Column(name = "a_id") private Integer aId; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public Integer getaId() { return aId; } public void setaId(Integer aId) { this.aId = aId; } @Override public String toString() { return "Comment{" + "id=" + id + ", content='" + content + '\'' + ", author='" + author + '\'' + ", aId=" + aId + '}'; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
在进行 Redis缓存 时,如果 不对 “缓存对象” 进行 “序列化” ,在进行缓存时,会报 IllegalArgumentException非法参数异常,提示信息要求对应的 Comment实体类必须实现序列化 : 实现 JDK自带 的 序列化接口 : Serializable 接口 。
( 一些基本数据类型,不需要序列化,因为内部已经默认实现了序列化接口 )
设置 “Redis缓存数据” 的 “有效期” :
application.properties :
spring.application.name=chapter_15 # mysql服务信息 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/springbootdata?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT&nullCatalogMeansCurrent=true spring.datasource.username=root spring.datasource.password=root #显示使用JPA进行"数据库查询"的SQL语句,用于展示操作的Sql语句 #这个属性决定了是否应该在控制台打印出SQL查询语句 spring.jpa.show-sql=true #Redis服务地址 spring.data.redis.host=127.0.0.1 #Redis服务器连接端口 spring.data.redis.port=6379 #Redis服务器连接密码(默认欸空) spring.data.redis.password=123456 #对基于注解的Redis缓存数据统一设置"有效期"为 1分钟,单位为"毫秒" spring.cache.redis.time-to-live= 60000
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
上述代码中,在 Spring Boot 全局配置文件中添加了“spring.cache.redis.time-to-live”属性统一配置 Redis 数据的有效期
( 单位为毫秒 ),这种方式相对来说不够灵活,并且 这种设置方式中将 “基于 APl的 Redis 缓存” 实现没有效果。
- 通过前面的操作,我们已经在项目中实现好了 关于 Redis 缓存管理 的 “配置” 和 “操作代码” , 并且在 项目启动类中 使用了 @EnableCaching开启了基于注解 "缓存管理" , 下面可进行 “Redis的缓存管理” 测试 ,访问controller类中的指定url进行 “缓存测试”。
- 在 SpringBoot 整合 Redis 缓存实现中,除了基于注解形式的 Redis 缓存实现外,还有一个开发中常用的方式 — 基于 “API” 的 “Redis缓存实现” ,下面将通过 Redis API 的方式讲解 SpringBoot 整合 Redis缓存的具体实现。
- ① RedisTemplate类 ( Redis模板类 )是 Spring 框架提供的用于与 Redis 数据库进行交互的工具类 ( 通过 该类 可以在 Java 中 "操作Redis数据库 " ) 。
- ② RedisTemplate 类 中 Spring Data Redis 提供的 直接进行Redis操作的Java API ( 通过该类来操作Redis数据库 ),可以直接注入使用,相比于Jedis 更加简便。
- ③ RedisTemplate 可以 操作 “对象类型” 数据,而其子类 StringRedisTemplate则是专门针对
“字符串类型” 的数据进行操作。 - ④ RedisTemplate 类中提供了很多操作Redis数据库 的 方法, 可以进行数据缓存查询、缓存更新、缓存修改、缓存删除以及设置缓存有效期等。
先创建了一个 数据库springbootdata,然后创建了两个表 t_article 和 t_comment ,并向表中插入数据。
其中评论表t_comment 的a_id 与文章表t_article 的主键id 相关联 ( t_article的主键作为t_comment表的 “外键”)。
项目的 目录结构 为 :
在配置"全局配置文件" : application.properties 中 “配置信息” :
application.properties :
# mysql服务信息 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/springbootdata?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT&nullCatalogMeansCurrent=true spring.datasource.username=root spring.datasource.password=root #使用JPA操作数据库时,在控制台上显示sql语句 spring.jpa.show-sql=true #Redis服务地址 spring.data.redis.host=127.0.0.1 #Redis服务器连接端口 spring.data.redis.port=6379 #Reids服务器连接密码(默认为空) spring.data.redis.password=123456
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
编写 “数据库表” 对应的 “实体类” ( 要实现"序列化" ) :
Comment.java :
import jakarta.persistence.*; import java.io.Serializable; //指定该实现类映射的数据库表 @Entity(name = "t_Comment") //设置ORM实体类, 并指定对应的表明 public class Comment implements Serializable { // implements Serializable : 进行序列化,存储数据进Redis数据库时需要 //表示数据库表中主键对应的属性 @Id @GeneratedValue(strategy= GenerationType.IDENTITY) //设置主键的生成策略 (主键自增) private Integer id; @Column(name = "content") //指定映射的表字段名 private String content; @Column(name = "author") private String author; @Column(name = "a_id") private Integer aId; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public Integer getaId() { return aId; } public void setaId(Integer aId) { this.aId = aId; } @Override public String toString() { return "Comment{" + "id=" + id + ", content='" + content + '\'' + ", author='" + author + '\'' + ", aId=" + aId + '}'; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
编写 “操作数据库” 的 Repository接口文件 :
CommentRepository.java :
import com.myh.chapter_17.domain.Comment; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; //Repository接口中为操作数据库的方法 /* 继承了JpaRepository接口,其中有操作数据库的curd方法,也用方法关键字的形式来操作数据库,或者使用@Query注解的方式来操作数据库 */ public interface CommentRepository extends JpaRepository<Comment,Integer> { }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
编写 "控制器层"的 controller 对象 :
ApiCommentController.java :
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController // 将该类加入到IOC容器中 + 将方法的返回值转换为指定的类型 存储到响应体中 @RequestMapping("/api") public class ApiCommentController { //控制层操作类 @Autowired private ApiCommentService apiCommentService; /** * 查询数据 */ @GetMapping("/get/{id}") //传递的参数为: 路径变量 public Comment findById(@PathVariable("id") int comment_id) { Comment comment = apiCommentService.findById(comment_id); return comment; } /** * 更新数据 */ @GetMapping("/update/{id}/{author}") public Comment updateComment(@PathVariable("id") int comment_id,@PathVariable("author") String author) { Comment updateComment = apiCommentService.updateComment(comment_id, author); return updateComment; } /** * 删除数据 */ @GetMapping("/delete/{id}") public void deleteComment(@PathVariable("id") int comment_id) { apiCommentService.deleteComment(comment_id); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
编写 "业务操作层"的 service 对象 :
ApiCommentService.java :
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Service; import java.util.Optional; import java.util.concurrent.TimeUnit; @Service //加入到IOC容器中 public class ApiCommentService { //业务操作类 @Autowired private CommentRepository commentRepository; //该接口继承了JPA相关的接口,通过其中的方法可操作Mysql数据库 @Autowired private RedisTemplate redisTemplate; //通过该类可在Java中操作Redis数据库 /** 从Mysql数据库中查询到数据后,将该数据存储到缓存中 (存储到Redis数据库中 ) */ public Comment findById(int comment_id) { //从Redis数据库中获取"缓存数据" ValueOperations valueOperations = redisTemplate.opsForValue(); Object object = valueOperations.get(comment_id); if (object != null) {//如果在Redis数据库中查询数据则返回 return (Comment) object; } else { //Reids中(缓存中)没有,进行数据库查询 Optional<Comment> optional = commentRepository.findById(comment_id); //判断从数据库中是否查询到数据 if (optional.isPresent()) { //获得该数据 Comment comment = optional.get(); //将从Mysql数据库中的查询结果进行"缓存" ( 缓存到Redis数据库中 ) , 设置有效期为1天 //设置的为Redis中的字符串数据 valueOperations.set(comment_id, comment, 1, TimeUnit.DAYS); //有效期为1天 return comment; } else { return null; } } } /** 更新Mysql数据库中的数据后,也更新缓存中的数据 (即同时也更新Redis数据库中的数据) */ public Comment updateComment(int comment_id,String author) { Comment comment = findById(comment_id); comment.setAuthor(author); comment = commentRepository.save(comment); //更新Mysql数据库中数据后,同时也更新缓存中的数据 ( 即更新Redis中的数据 ) ValueOperations valueOperations = redisTemplate.opsForValue(); valueOperations.set(comment_id,comment); return comment; } /** * 删除Mysql数据库中数据后,也删除Redis中的缓存数据 */ public void deleteComment(int comment_id) { //commentRepository.deleteById(comment_id); System.out.println("Mysql数据库中对应的数据删除成功....."); //删除Mysql数据库中数据后,也删除Redis数据库中的"缓存数据" Boolean commentId = redisTemplate.delete(comment_id); if (commentId) { System.out.println("Redis数据库中对应的缓存数据-删除成功....."); } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 基于 API ( RedisTemplate类 ) 的 “缓存管理” 测试 :
访问 controller类中的url进行测试即可。