web应用程序开发案例分析实验六 Spring Boot缓存管理

实验六 Spring Boot缓存管理

一、实验目的

1、了解MyBatis的基础知识

2、熟悉MyBatis的工作原理

3、掌握MyBatis入门程序的编写

二、实验内容

1、通过实际开发介绍,引入数据缓存管理的重要。

2、根据spring的基本应用,练习Spring Boot整合Redis实现缓存管理,并进行相关组件定制。

三、实验步骤

第一部分实验 Speing Boot缓存管理

(一)基础环境搭建

1.准备数据,创建项目

(1)创建Spring Boot项目,引入相关依赖

①这里使用之前创建的springbootdata的数据库,该数据库有两个表 t_article和t_comment,这两个表预先插入几条测试数据。

②使用Spring Initializr方式创建一个Spring Boot项目,在Dependencies依赖选择项中JPA依赖、MySQL依赖和Web依赖。

(2)编写数据库表对应的实体类

在com.lg.ch06文件夹下创建domain文件夹并在其中创建实体类Comment.java

import javax.persistence.*;

@Entity(name = "t_comment")
public class Comment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String content;
    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 +
                '}';
    }
}

(3)编写数据库操作的Repository接口文件CommentRepository

在com.sun.ch06文件夹下创建repository文件夹并在其中创建接口文件CommentRepository

import com.lg.ch06.domain.Comment;
import org.springframework.data.jpa.repository.*;
import org.springframework.transaction.annotation.Transactional;

public interface CommentRepository extends JpaRepository<Comment,Integer> {
    @Transactional
    @Modifying
    @Query("UPDATE t_comment c SET c.author= ?1 WHERE  c.id = ?2")
    public int updateComment(String author,Integer id);
}

(4)编写业务操作类Service文件CommentService,在该类中编写数据的查询、修改和删除操作

在com.lg.ch06文件夹下创建service文件夹并在其中创建CommentService.java

import com.lg.ch06.domain.Comment;
import com.lg.ch06.repository.CommentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;

@Service
public class CommentService {
    @Autowired
    private CommentRepository commentRepository;
    public Comment findById(int comment_id){
        Optional<Comment> optional = commentRepository.findById(comment_id);
        if(optional.isPresent()){
            return optional.get();
        }
        return null;
    }

    // 更新评论信息
    public Comment updateComment(Comment comment){
        commentRepository.updateComment(comment.getAuthor(), comment.getaId());
        return comment;
    }
    // 删除评论信息
    public void deleteComment(int comment_id){
        commentRepository.deleteById(comment_id);
    }
}

(5)编写Web访问层Controller文件CommentController,使用注入的CommentService实例对象编写对Comment评论数据的查询、修改和删除方法。

在com.lg.ch06文件夹下创建controller文件夹并在其中创建CommentController.java文件

import com.lg.ch06.domain.Comment;
import com.lg.ch06.service.CommentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
public class CommentController {
    @Autowired
    private CommentService commentService;
    @GetMapping("/get/{id}")
    public Comment findById(@PathVariable("id") int comment_id){
        Comment comment = commentService.findById(comment_id);
        return comment;
    }

    @GetMapping("/update/{id}/{author}")
    public Comment updateComment(@PathVariable("id") int comment_id,
                                 @PathVariable("author") String author){
        Comment comment = commentService.findById(comment_id);
        comment.setAuthor(author);
        Comment updateComment = commentService.updateComment(comment);
        return updateComment;
    }

    @GetMapping("/delete/{id}")
    public void deleteComment(@PathVariable("id") int comment_id){
        commentService.deleteComment(comment_id);
    }
}

2.编写配置文件application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/springbootdata?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=1234
spring.jpa.show-sql=true

3.项目测试

项目启动成功后,在浏览器上访问http://127.0.0.1:8080/get/1,浏览器每刷新一次,控制台会新输出一条SQL语句

(二)Spring Boot 默认缓存体验

1.使用@EnableCaching注解开启基于注解的缓存支持

修改Ch06Application.java文件

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@EnableCaching
@SpringBootApplication
public class Ch06Application {

    public static void main(String[] args) {
        SpringApplication.run(Ch06Application.class, args);
    }

}

2.使用@Cacheable注解对数据操作方法进行缓存管理

修改service文件夹下的CommentService.java中findById方法

@Cacheable(cacheNames = "comment")
public Comment findById(int comment_id){
    Optional<Comment> optional = commentRepository.findById(comment_id);
    if(optional.isPresent()){
        return optional.get();
    }
    return null;
}

3.Spring Boot默认缓存测试

项目启动成功后,在浏览器上访问http://127.0.0.1:8080/get/1,不论浏览器刷新多少次,页面的查询结果都会显示同一条数据

第二部分实验Spring Boot整合Redis缓存实现

(一)SpringBoot支持的缓存组件

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(默认) 

(二)基于注解的Redis缓存实现

1.添加Spring Data Redis 依赖启动器

<dependency> 
	<groupId>org.springframework.boot</groupId> 
	<artifactId>spring-boot-starter-data-redis</artifactId> 
</dependency> 

2.Redis服务连接配置

spring.redis.host=127.0.0.1 
spring.redis.port=6379 
spring.redis.password= 

3.使用@Cacheable、@CachePut、@CacheEvict注解定制缓存管理

对CommentService类中的方法进行修改

修改service文件夹下的CommentService.java

import com.lg.ch06.domain.Comment;
import com.lg.ch06.repository.CommentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.Optional;

@Service
public class CommentService {
    @Autowired
    private CommentRepository commentRepository;
    @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){
        commentRepository.updateComment(comment.getAuthor(), comment.getaId());
        return comment;
    }
    @CacheEvict(cacheNames = "comment")
    public void deleteComment(int comment_id){
        commentRepository.deleteById(comment_id);
    }
}

4.基于注解的Redis查询缓存测试

项目启动成功后,通过浏览器访问http://127.0.0.1:8080/get/1

5.将缓存对象实现序列化

修改domain下的Comment.java,添加Serializable接口

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;
    @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 +
                '}';
    }
}

6.基于注解的Redis缓存查询测试

项目启动成功后,通过浏览器访问http://127.0.0.1:8080/get/1,并重复刷新浏览器查询同一条数据信息

重复进行同样的查询操作,数据库只执行了一次SQL语句

打开Redis客户端可视化管理工具Redis Desktop Manager连接本地启用的Redis服务

7.基于注解的Redis缓存更新测试

项目启动成功后,通过浏览器访问http://127.0.0.1:8080/update/1/shitou,接着,继续访问http://127.0.0.1:8080/get/1

执行updateComment()方法更新id为1的数据时执行了一条更新SQL语句,后续调用findById()方法查询id为1的用户评论信息时没有执行查询SQL语句。

8.基于注解的Redis缓存删除测试

项目启动成功后,通过浏览器访问http://127.0.0.1:8080/delete/1,接着,继续访http://127.0.0.1:8080/get/1

通过Redis客户端可视化管理工具Redis Desktop Manager查看对应数据删除后的缓存信息

(三)基于API的Redis缓存实现

1.使用Redis API 进行业务数据缓存管理

在service文件夹下编写一个进行业务处理的类ApiCommentService

import com.lg.ch06.domain.Comment;
import com.lg.ch06.repository.CommentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Service
public class ApiCommentService {
    @Autowired
    private CommentRepository commentRepository;
    @Autowired
    private RedisTemplate redisTemplate;

    public Comment findById(int comment_id){
        // 先从Redis缓存中查询数据
        Object object =  redisTemplate.opsForValue().get("comment_"+comment_id);
        if (object!=null){
            return (Comment)object;
        }else {
            // 缓存中没有,就进入数据库查询
            Optional<Comment> optional = commentRepository.findById(comment_id);
            if(optional.isPresent()){
                Comment comment= optional.get();
                // 将查询结果进行缓存,并设置有效期为1天
                redisTemplate.opsForValue().set("comment_"+comment_id, comment,1, TimeUnit.DAYS);

                return comment;
            }else {
                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);
    }
}

2.编写Web访问层Controller文件

在controller下创建ApiCommentController.java

import com.lg.ch06.domain.Comment;
import com.lg.ch06.service.ApiCommentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@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 comment = apiCommentService.findById(comment_id);
        comment.setAuthor(author);
        Comment updateComment = apiCommentService.updateComment(comment);
        return updateComment;
    }

    @GetMapping("/delete/{id}")
    public void deleteComment(@PathVariable("id") int comment_id){
        apiCommentService.deleteComment(comment_id);
    }
}

3.基于API的Redis缓存实现的相关配置

(1)基于API的Redis缓存实现不需要@EnableCaching注解开启基于注解的缓存支持。

(2)基于API的Redis缓存实现需要在Spring Boot项目的pom.xml文件中引入Redis依赖启动器,并在配置文件中进行Redis服务连接配置,同时将进行数据存储的Comment实体类实现序列化接口。

(3)缓存测试与基于注解的Redis缓存实现的测试完全一样。

① 查询测试

项目启动成功后,通过浏览器访问http://127.0.0.1:8080/get/2,并重复刷新浏览器查询同一条数据信息

② 更新测试

项目启动成功后,通过浏览器访问http://127.0.0.1:8080/update/2/shitou,接着,继续访问http://127.0.0.1:8080/get/2 

③ 删除测试

项目启动成功后,通过浏览器访问http://127.0.0.1:8080/delete/2,接着,继续访http://127.0.0.1:8080/get/2

 

第三部分实验 自定义Redis缓存序列化机制

(一)自定义Redis Template

1.Redis API 默认序列化机制

基于Redis API的Redis缓存实现是使用RedisTemplate模板进行数据缓存操作的,这里打开RedisTemplate类,查看源码可知:

(1)使用RedisTemplate进行Redis数据缓存操作时,内部默认使用的是JdkSerializationRedisSerializer序列化方式,所以进行数据缓存的实体类必须实现JDK自带的序列化接口(例如Serializable);

(2)使用RedisTemplate进行Redis数据缓存操作时,如果自定义了缓存序列化方式defaultSerializer,那么将使用自定义的序列化方式。

2.自定义RedisTemplate序列化机制

在项目中引入Redis依赖后,Spring Boot提供的RedisAutoConfiguration自动配置会生效。打开RedisAutoConfiguration类,查看内部源码中关于RedisTemplate的定义方式可知:

在Redis自动配置类中,通过Redis连接工厂RedisConnectionFactory初始化了一个RedisTemplate;该类上方添加了@ConditionalOnMissingBean注解(顾名思义,当某个Bean不存在时生效),用来表明如果开发者自定义了一个名为redisTemplate的Bean,则该默认初始化的RedisTemplate会被覆盖。

如果想要使用自定义序列化方式的RedisTemplate进行数据缓存操作,可以参考上述核心代码创建一个名为redisTemplate的Bean组件,并在该组件中设置对应的序列化方式即可。

在com.lg.ch06文件夹下创建config文件夹并在其中创建RedisConfig.java

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

@Configuration   // 定义一个配置类
public class RedisConfig {
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        // 使用JSON格式序列化对象,对缓存数据key和value进行转换
        Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
        // 解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSeial.setObjectMapper(om);

        // 设置RedisTemplate模板API的序列化方式为JSON
        template.setDefaultSerializer(jacksonSeial);
        return template;
    }
}

项目启动成功后,通过浏览器访问http://127.0.0.1:8080/api/get/3,并重复刷新浏览器查看同一条数据信息

执行findById()方法正确查询出用户评论信息Comment,重复进行同样的查询操作,数据库只执行了一次SQL语句

使用Redis客户端可视化管理工具Redis Desktop Manager查看缓存数据

缓存到Redis服务的数据已经使用了JSON格式存储展示

(二)自定义RedisCacheManager

1.Redis 注解默认序列化机制

(1)Spring Boot整合Redis组件提供的缓存自动配置类RedisCacheConfiguration,其内部是通过Redis连接工厂RedisConnectionFactory定义了一个缓存管理器RedisCacheManager;同时定制RedisCacheManager时,也默认使用了JdkSerializationRedisSerializer序列化方式。

(2)如果想要使用自定义序列化方式的RedisCacheManager进行数据缓存操作,可以创建一个名为cacheManager的Bean组件,并在该组件中设置对应的序列化方式即可。

2.自定义RedisCacheManager

在config下的RedisConfig中添加自定义组件

@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
    // 分别创建String和JSON格式序列化对象,对缓存数据key和value进行转换
    RedisSerializer<String> strSerializer = new StringRedisSerializer();
    Jackson2JsonRedisSerializer jacksonSeial =
            new Jackson2JsonRedisSerializer(Object.class);
    // 解决查询缓存转换异常的问题
    ObjectMapper om = new ObjectMapper();
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    jacksonSeial.setObjectMapper(om);

    // 定制缓存数据序列化方式及时效
    RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofDays(1))
            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(strSerializer))
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jacksonSeial))
            .disableCachingNullValues();

    RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(config).build();
    return cacheManager;
}

3.效果测试

项目启动成功后,通过浏览器访问http://localhost:8080/api/get/3,并重复刷新浏览器查看同一条数据信息

执行findById()方法正确查询出用户评论信息Comment,重复进行同样的查询操作,数据库只执行了一次SQL语句

使用Redis客户端可视化管理工具Redis Desktop Manager查看缓存数据

缓存到Redis服务的数据已经使用了JSON格式存储展示

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小孙同学1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值