springboot高级特性-缓存

Posted 暴躁的程序猿啊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot高级特性-缓存相关的知识,希望对你有一定的参考价值。

缓存:将相应数据存储起来以避免数据的重复创建、处理和传输,可有效提高性能

springboot中使用缓存 可以缓存方法的返回值 等等 避免多次查询数据库

springboot的缓存有以下层级关系
CachingProvider 缓存提供者 —>管理和控制多个CacheManager---->管理和控制多个唯一命名的cache----->存储在cache中的key-value对-------->Expiry 每一个存储在cache中的条目有一个有效期

Spring缓存抽象

cache 定义各种操作

CacheManager 缓存管理器,管理各种缓存组件

Cache缓存接口 定义缓存操作 如:RedisCache。EhCacheCaceh等

缓存注解

@Cacheable 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存

@CacheEvict 清空缓存

@CachePut 保证方法被调用,又希望结果被缓存

@EnableCaching 开启基于注解的缓存

keyGenerator 缓存数据时key生成策略

serialize 缓存数据时value序列化策略

我们要想使用缓存 导入缓存依赖

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

创建一个可以访问数据库的工程
pojo类

/**
 * @create: 2021/9/4
 * @author: Tony Stark
 */
@Data
public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private Integer gender;  //性别  1男  0女
}

定义mapper

@Mapper
public interface EmployeeMapper {

    @Select("select * from employee where id =#{id}")
    public Employee getEmpById(Integer id);
}

service

@Service
public class EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;

    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee empById = employeeMapper.getEmpById(id);
        return empById;
    }
}

controller

@RestController
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;

    @GetMapping("/emp/{id}")
    public Employee getEmployee(@PathVariable("id")Integer id){
        Employee emp = employeeService.getEmp(id);
        return emp;
    }
}

如果我们要使用缓存需要在启动类上加EnableCaching注解开启缓存

@SpringBootApplication
@EnableCaching
public class Springboot01CacheApplication {

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

}

将方法的运行结果进行缓存;以后再有相同的数据,直接从缓存中取,不用去调方法;
在方法上加上cacheable注解缓存结果
@Cacheable(cacheNames = “emp”)

 @Cacheable(cacheNames = "emp")
    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee empById = employeeMapper.getEmpById(id);
        return empById;
    }

访问测试

http://localhost:8080/emp/1

第一次访问

我们清空控制台进行 第二次访问

可以看到并没有查询数据库 而是使用了缓存 大家可以多试几次

方法的属性 :
**cacheNames/value 😗*指定缓存的名字;将方法的返回结果放在哪个缓存中,是数组的方式可以指定多个缓存
cacheManager管理多个cache组件的,对缓存的真正CRUD操作在cache组件中,每一个缓存有自己唯一一个名字
key: 缓存数据时的key;可以用它来指定 默认使用方法参数的值 值就是方法的返回值
#id 代表取出取出参数id的值作为key
例如 参数传入进来 1 存储为 1-返回值
编写SpEL:#id 参数id的值 #a0 #p0 #root.args[0]

KeyGenerator: key的生成器 : 可以指定key的生成器的组件id
key/KeyGenerator二选一使用
cacheManager:指定缓存管理器 或者cacheResolver指定缓存解析器

condition: 指定符合条件的情况下才缓存
condition = “#id>0” 指定id大于0的时候才进行缓存

 @Cacheable(cacheNames = "emp",condition = "#id>2")
    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee empById = employeeMapper.getEmpById(id);
        return empById;
    }

我现在指定的id>2时才进行缓存
测试一下
第一次访问

第二次访问 可以看出并没有进行缓存

unless: 否定缓存; 当unless指定的条件为true方法的返回值就不回被缓存;可以获取到结果进行判断
unless = “#result == null” 当结果为空时就不进行缓存

sync: 是否使用异步模式 注意开启异步 unless就会失效

@CachePut 注解

加入mapper方法

  @Update("update employee  set lastName=#{lastName},email=#{email},gender=#{gender},d_id=#{dId}where id=#{id}")
    public void updateEmp(Employee employee);

@CachePut : 即调用方法又更新缓存数据
修改了数据库的某个数据,同时更新缓存
运行时机:先调用目标方法把方法的返回值缓存起来

   @CachePut(cacheNames = "emp")
    public Employee updateEmployee(Employee employee){
        employeeMapper.updateEmp(employee);
        return employee;
    }

测试步骤

先查询一个员工 查到的结果会放到缓存中

http://localhost:8080/emp/1


以后查询的结果还是之前缓存的结果

这时候我们更新这个员工

http://localhost:8080/get?id=1&lastName=&email=王五2858458@qq.com&dId=1&gender=1

更新方法将方法的返回值也放在了缓存中

当我们再次查询这个员工的时候 数据是更新之后的 还是更新之前的值

http://localhost:8080/emp/1

这是为什么呢 难道我们的@CachePut注解没生效??

其实不是的

我们缓存的时候key默认为方法的参数 我们查询的参数为 id 所以缓存key为id id-value
我们更新的方法参数为employee 所以缓存key为employee employee-value
跟我们查询的时候不是一个缓存 所以导致结果没更新 我们需要指定key为同一个可以即可

    @CachePut(cacheNames = "emp",key = "#employee.id")
    public Employee updateEmployee(Employee employee){
        employeeMapper.updateEmp(employee);
        return employee;
    }

或者
因为我们的 @CachePut注解 是方法返回结果后缓存所以我们可以从方法的返回值中获取

 @CachePut(cacheNames = "emp",key = "#result.id")
    public Employee updateEmployee(Employee employee){
        employeeMapper.updateEmp(employee);
        return employee;
    }

当然我们的@Cacheable注解是不能用result的 因为我们的@Cacheable注解要在目标方法执行之前按照key去查询缓存 所以没有方法返回的result的结果

这时候我们重新测试

http://localhost:8080/emp/1


我们更新

http://localhost:8080/get?id=1&lastName=wangwu&email=2858458@qq.com&dId=1&gender=1


这时候我们再查询看看会不会更新

答案是已经更新了 且还是缓存中的值没有查询数据库

@CacheEvict注解

@CacheEvict : 缓存清除
通过可以指定要清除的数据 不写默认是参数为key
key = "#id"取出id为key的缓存
参数:allEntries = true 是指定是否删除缓存中的所有数据默认为false true时就是删除缓存中所有数据
beforeInvocation = true 缓存的清除是否在方法之前执行
默认值为true代表在方法执行之后执行 这里需要注意的是 如果是在方法执行之后执行 如果方法抛出了异常 缓存就不会清空。
添加删除的方法

  @GetMapping("/deleteEmp/{id}")
    public String deleteEmp(@PathVariable("id") Integer id){
        employeeService.deleteEmp(id);
        return "删除成功";
    }
     public void deleteEmp(Integer id){
         System.out.println("删除员工"+id);
         employeeMapper.deleteEmpById(id);
     }

添加注解

 @CacheEvict(value = "emp",key = "#id" )
     public void deleteEmp(Integer id){
         System.out.println("删除员工"+id);
         employeeMapper.deleteEmpById(id);
     }

测试

我们先拿缓存

这是调用删除缓存方法

http://localhost:8080/deleteEmp/1


我们再次查询数据 发现我们的数据是从数据库中拿的不是缓存

@CacheConfig注解

@CacheConfig(cacheNames = “emp”)
可以指定全局的配置 标注在类上该类的所有公共注解只需在类上配置一次即可

@CacheConfig(cacheNames = "emp")
@Service
public class EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;
    @Cacheable(condition = "#id>0")
    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee empById = employeeMapper.getEmpById(id);
        return empById;
    }
    /**
     * @CachePut  :  即调用方法又更新缓存数据
     * 修改了数据库的某个数据,同时更新缓存
     * 运行时机:先调用目标方法把方法的返回值缓存起来
     * key = "#result.id"
     */
    @CachePut(key = "#employee.id")
    public Employee updateEmployee(Employee employee){
        employeeMapper.updateEmp(employee);
        return employee;
    }
  
    @CacheEvict(key = "#id",beforeInvocation = true)
     public void deleteEmp(Integer id){
        employeeMapper.deleteEmpById(id);
     }
 }

以上是关于springboot高级特性-缓存的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis高级特性

RabbitMQ——高级特性(SpringBoot实现)

如何使用yii2的缓存依赖特性

springboot高级特性之邮件发送

SpringBoot 参数校验,高级特性,非常实用

SpringBoot 参数校验,高级特性,非常实用!