Spring Boot 2.x实战76 - Spring Data 20 - 数据缓存(Redis Cache 非仅限于Spring Data)

4.数据缓存

4.1 Spring Boot与缓存

缓存服务数据能极大的提升应用的性能。使用Spring缓存需使用@EnableCaching开启支持,使用注解:

  • @Cacheable:注解方法可以被缓存;@Cacheable对于特定缓存key,方法只会执行一次,后续的请求将不执行方法而从缓存中取数据;
  • @CachePut:注解方法触发缓存添加操作;@CachePut注解的方法每次请求都会执行;
  • @CacheEvict:注解方法触发从缓存中移除旧数据的操作;
  • @Caching:支持组合@Cacheable@CachePut@CacheEvict组合注解在一个方法上;
  • @CacheConfig:类级别的共享配置。

一旦开启了缓存的支持,Spring Boot会按照下面的顺序查找缓存提供者,使用每个缓存技术需要配置相关的CacheManager的Bean:

  • Generic:SimpleCacheManager

  • JCache(JSR-107):JCacheCacheManager

  • EhCache 2.x:EhCacheCacheManager

  • Hazelcast:HazelcastCacheManager

  • Infinispan:SpringEmbeddedCacheManager

  • CouchBase:CouchbaseCacheManager

  • Redis:RedisCacheManager

  • Caffeine:CaffeineCacheManager

  • Simple:ConcurrentMapCacheManager

Spring Boot通过CacheAutoConfiguration实现对这些缓存提供技术的CacheManager的自动配置;我们可以通过使用CacheProperties提供的spring.cache.*来定制一些配置。

spring.cache.type: redis #设置默认缓存提供者

Redis是知名的内存数据库,也是优秀的缓存提供者,从内存中获取数据将极大的提升系统的性能;Spring Boot通过CacheAutoConfiguration自动加载了RedisCacheConfiguration,而RedisCacheConfiguration自动配置了RedisCacheManager的Bean。

Redis相关库在类路径的时候,Spring Boot也会自动使用RedisAutoConfiguration,通过RedisProperties提供spring.redis.*来配置连接Redis。

4.2 环境准备

4.2.1 安装Redis

使用docker compose安装Redis。

stack.yml

version: '3.1'

services:
  redis:
    image: 'bitnami/redis:5.0'
    environment:
      - REDIS_PASSWORD=zzzzzz
    ports:
      - '6379:6379'

执行命令:

$ docker-compose -f stack.yml up -d
4.2.2 新建项目

新建应用,信息如下:

Group:top.wisely

Artifact:learning-caching

Dependencies:Spring Cache abstractionSpring Data RedisSpring Web StarterSpring Data JPAMySQL DriverLombok

build.gradle文件中的依赖如下:

dependencies {
   implementation 'org.springframework.boot:spring-boot-starter-cache'
   implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
   implementation 'org.springframework.boot:spring-boot-starter-data-redis'
   implementation 'org.springframework.boot:spring-boot-starter-web'
   compileOnly 'org.projectlombok:lombok'
   runtimeOnly 'mysql:mysql-connector-java'
   annotationProcessor 'org.projectlombok:lombok'
  //...
}

我们的外部配置如下:

spring:
  cache:
    type: redis
  redis: # 连接redis
    host: localhost
    password: zzzzzz
    port: 6379
  datasource: # 连接数据库
    url: jdbc:mysql://localhost:3306/first_db?useSSL=false
    username: root
    password: zzzzzz
    driver-class-name: com.mysql.cj.jdbc.Driver
    initialization-mode: always
    continue-on-error: true
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update

测试的实体类为:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Student implements Serializable {
    @Id
    @GeneratedValue
    private Long id;
    private String name;

    public Student(String name) {
        this.name = name;
    }
}

Repository为:

public interface StudentRepository extends JpaRepository<Student, Long> {
}

测试数据data.sql

insert into student values (1, "xxx");

4.3 使用缓存注解

我们使用Service来演示缓存注解:

@Service
@CacheConfig(cacheNames = "student") //1 
public class StudentService {

    StudentRepository studentRepository;

    public StudentService(StudentRepository studentRepository) {
        this.studentRepository = studentRepository;
    }

    @CachePut(key = "#student.id") //2
    public Student saveOrUpdate(Student student){
        return studentRepository.save(student);
    }

    @CacheEvict //3
    public void remove(Long id){
        studentRepository.deleteById(id);
    }

    @Cacheable(unless = "#result == null") //4
    public Optional<Student> findOne(Long id){
        return studentRepository.findById(id);
    }


}
  1. @CacheConfig设置缓存名称作为整个类级别的共享配置,类中的方法注解可省略指定缓存名称;
  2. 缓存注解默认使用参数作为缓存的key,我们可以用SPEL从参数对象中获取值,如获得studentid可以使用key = "#student.id"@CachePut将key为student的id,值为返回值的数据存入缓存;
  3. @CacheEvict删除以方法中的参数(即id)为key的值;
  4. 当key(方法参数的id)第一次请求到此方法的时候,查询数据库并将返回值放入缓存中;后续相同key的请求将直接从缓存中获取;unless可做否决,当表达式为true时不存入缓存,表达式为false存入缓存;unless在方法执行完成后评估,可以获得方法的返回值result,返回值为Optional时,result仍然是其包装的studentunless中的表达式的含义为:返回值如果为空则不放入缓存。
4.3.1 验证@Cacheable
@Bean
CommandLineRunner cacheableOperation(StudentService studentService){
   return args -> {
      System.out.println("第一次查询,需要查询数据,然后缓存数据,方法被调用,有查询SQL");
      System.out.println(studentService.findOne(1l));
      System.out.println("第一次查询,从缓存中获取数据而不调用方法,下面不会有SQL");
      System.out.println(studentService.findOne(1l));
   };
}

在这里插入图片描述

4.3.2 验证@CachePut
@Bean
CommandLineRunner cachePutOperation(StudentService studentService){
   return args -> {
      Student student = studentService.saveOrUpdate(new Student("wyf"));
      Long id = student.getId();
      System.out.println("因使用@CachePut已经将key为id的值存入了缓存,下面的查询将不会有SQL");
      System.out.println(studentService.findOne(id));
      
      student.setName("wangyunfei");
      studentService.saveOrUpdate(new Student("wyf"));
      System.out.println("@CachePut在修改时会被调用,缓存新的值,下面的查询也将不会有SQL");
      System.out.println(studentService.findOne(id));
   };
}

在这里插入图片描述

4.4.3 验证@CacheEvit
@Bean
CommandLineRunner cacheEvitOperation(StudentService studentService){
   return args -> {
      studentService.remove(1l);
      System.out.println("通过@CacheEvict删除了key为id的缓存,所以下面的查询会有SQL");
      studentService.findOne(1l);
   };
}

在这里插入图片描述

新书推荐:

我的新书《从企业级开发到云原生微服务:Spring Boot 实战》已出版,内容涵盖了丰富Spring Boot开发的相关知识
购买地址:https://item.jd.com/12760084.html
在这里插入图片描述
主要包含目录有:

第一章 初识Spring Boot(快速领略Spring Boot的美丽)
第二章 开发必备工具(对常用开发工具进行介绍:包含IntelliJ IDEA、Gradle、Lombok、Docker等)
第三章 函数式编程
第四章 Spring 5.x基础(以Spring 5.2.x为基础)
第五章 深入Spring Boot(以Spring Boot 2.2.x为基础)
第六章 Spring Web MVC
第七章 数据访问(包含Spring Data JPA、Spring Data Elasticsearch和数据缓存)
第八章 安全控制(包含Spring Security和OAuth2)
第九章 响应式编程(包含Project Reactor、Spring WebFlux、Reactive NoSQL、R2DBC、Reactive Spring Security)
第十章 事件驱动(包含JMS、RabbitMQ、Kafka、Websocket、RSocket)
第11章 系统集成和批处理(包含Spring Integration和Spring Batch)
第12章 Spring Cloud与微服务
第13章 Kubernetes与微服务(包含Kubernetes、Helm、Jenkins、Istio)
多谢大家支持。

猜你喜欢

转载自blog.csdn.net/wiselyman/article/details/106780112