Spring Boot缓存注解

一、JSR107规范

Java Caching定义的5个接口:CachingProvider,CacheManager,Cache,ENtry,Expiry

  • CachingProvider:定义了创建、配置、获取、管理和控制多个CacheManager,一个应用可以在运行期间访问多个CachingProvider
  • CacheManager:定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中,一个CacheManager仅被一个CachingProvider所拥有
  • Cache:一个类似Map的数据结构并临时存储以Key为索引的值,一个Cache仅被一个CacheManager所拥有(从缓存管理器中获取缓存,可以进行增删改查)
  • Entry:一个存储Cache中的Key-value对
  • Expiry:每一个存储在Cache中的条目有一个定义的有效期,一旦超过这个时间,条目为过期状态,一旦过期,条目将不可访问、更新和删除,缓存有效期可以通过ExpiryPolicy设置
    在这里插入图片描述

二、Spring缓存抽象

Spring定义了org.springframewrok.cache.Cache、org.springframewrok.cache.CacheManager接口来统一不同的缓存技术,并支持使用JSR107注解。

  • Cache:缓存接口,定义缓存操作,Cache接口下Spring提供了各种xxxCache的实现:RedisCache、EhCacheCache、ConcurrentMapCache等
  • CacheManager:缓存管理器,管理各种缓存(Cache)组件
  • @Cacheable:主要针对方法进行配置,能够根据方法的请求参数对其结果进行缓存(例如:public User getUser(Integer id) 注解之后,可以将返回的User对象存入缓存,如果已经存在则不会调用此方法)
  • @CacheEvict:清空缓存
  • @CachePut:更新缓存,保证方法被调用,又希望结果被缓存(例如:注解 public User updateUser(User user) ,此方法一定会被调用,且会更新到缓存)
  • @EnableCaching:开启基于注解的缓存
  • keyGenerator:缓存数据时key生成策略
  • seriablize:缓存数据时value序列化策略

三、缓存环境搭建

1 搭建基本环境:

1、导入数据库文件,创建department和employee表
2、创建javabean封装数据
在这里插入图片描述
3、整合MyBatis操作数据库
(1)application.properties中配置数据源信息

spring.datasource.url=jdbc:mysql://192.168.0.106:3306/spring_cache
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

(2)使用注解版的mybatis
@MapperScan指定需要扫描的mapper接口所在的包

import springboot.bean.Employee;
import org.apache.ibatis.annotations.*;

@Mapper
public interface EmployeeMapper {

    @Select("SELECT * FROM employee WHERE id = #{id}")
    public Employee getEmpById(Integer id);

    @Update("UPDATE employee SET lastName=#{lastName},email=#{email},gender=#{gender},d_id=#{dId} WHERE id=#{id}")
    public void updateEmp(Employee employee);

    @Delete("DELETE FROM employee WHERE id=#{id}")
    public void deleteEmpById(Integer id);

    @Insert("INSERT INTO employee(lastName,email,gender,d_id) VALUES(#{lastName},#{email},#{gender},#{dId})")
    public void insertEmployee(Employee employee);

    @Select("SELECT * FROM employee WHERE lastName = #{lastName}")
    Employee getEmpByLastName(String lastName);
}
import springboot.bean.Department;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface DepartmentMapper {

    @Select("SELECT * FROM department WHERE id = #{id}")
    Department getDeptById(Integer id);
}

(3)配置@Service

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import springboot.bean.Employee;
import springboot.mapper.EmployeeMapper;

@Service
public class EmployeeService {

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

(4)配置控制类,与web进行交互

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.RestController;

import springboot.bean.Employee;
import springboot.mapper.EmployeeMapper;
import springboot.service.EmployeeService;

@RestController
public class EmployeeController {
	
	@Autowired
	EmployeeService employeeservice;
	
	@GetMapping("/emp/{id}")
	public Employee getEmployee(@PathVariable("id") Integer id) {
		Employee emp=employeeservice.getEmp(id);
		return emp;
	}
}
2 快速体验缓存:

步骤:

  1. 在main中开启基于注解的缓存@EnableCaching
@MapperScan("springboot.mapper")
@SpringBootApplication
@EnableCaching
public class SpringBoot0CacheApplication {

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

}
  1. 标注缓存注解@Cacheable(对方法进行配置)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import springboot.bean.Employee;
import springboot.mapper.EmployeeMapper;

@Service
public class EmployeeService {

	@Autowired
	EmployeeMapper employeeMapper;
	
	/**
	 * 将方法的运行结果进行缓存,以后再需要相同的数据,可以从缓存中获取,不用调用方法从数据库获取
	 * 
	 * CacheManager管理多个Cache组件,对缓存的真正CRUD操作在Cache组件中,
	 * 每一个缓存组件有自己唯一的名字
	 * 
	 * Cacheable中的几个属性:
	 *       cacheNames/value:指定缓存组件的名字(例如有专门给员工的缓存、部门的缓存);将方法的返回结果放在哪个缓存中,以数组的方式,可以指定多个缓存
	 *       key:缓存数据时使用的key:可以进行指定key的方式(key="#root.methodName+'['+#id+']'"),默认是此使用方法的参数(key=id=1)
	 *       keyGenerator:key的生成器,可以自己指定key的生成器的组件id          
	 *            keyGenerator/key:二选一使用
	 *       CacheManager:指定缓存管理器;或者cacheResolver指定缓存解析器(二选一)
	 *       condition:指定符合条件的情况下(为true时)才缓存,condition = "#id>0"表示当id>0时才进行缓存
	 *       unless:否定缓存:当unLess指定的条件为true,方法的返回值就不会被缓存
	 * 	     sync:是否使用异步模式
	 * 
	 * @param id
	 * @return
	 */
	@Cacheable(cacheNames="emp",condition = "#id>0",unless = "#result==null")  //如果返回的结果为null,就不缓存了
	public Employee getEmp(Integer id) {
		System.out.println("查询"+id+"号员工");
		Employee emp=employeeMapper.getEmpById(id);
		return emp;
	}
}

第一次在浏览器中输入:http://localhost:8080/emp/1时,会调用getEmp方法,从数据库中查询信息,再次查询1号时,会从缓存中根据key(id=1)进行查询1号,而不会再调用getEmp方法。

自定义KeyGenerator方法:

import java.lang.reflect.Method;
import java.util.Arrays;

import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration  //配置类
public class MyCacheConfig {

	@Bean("myKeyGenerator")  //加入到容器中
	public KeyGenerator keyGenerator() {
		return new KeyGenerator() {

			@Override
			public Object generate(Object target, Method method, Object... params) {
				// TODO Auto-generated method stub
				return method.getName()+"["+Arrays.asList(params).toString()+"]";
			}
		};
	}
}

调用时在@Cacheable(cacheNames="emp",condition = "#id>0",unless = "#result==null")中将参数key变成keyGenerator=“myKeyGenerator”

3 缓存中的其他注解说明:

(1)@CachePut
既调用方法,又更新缓存数据(一定会调用该被注解的方法updataEmp)
运行时机:先调用注解的方法updataEmp,再修改数据库的某个数据,同时更新缓存,将目标方法的结果缓存起来。(key="#employee.id"为用对象的id作为key进行缓存)

@Cacheable(cacheNames="emp",condition = "#id>0",unless = "#result==null")  //如果返回的结果为null,就不缓存了
	public Employee getEmp(Integer id) {
		System.out.println("查询"+id+"号员工");
		Employee emp=employeeMapper.getEmpById(id);
		return emp;
	}
	
	@CachePut(cacheNames="emp",key="#employee.id")
	public Employee updataEmp(Employee employee) {
		System.out.println("更新:"+employee);
		employeeMapper.updateEmp(employee);
		return employee;
	}

在控制类中添加:

@GetMapping("/emp")
	public Employee update(Employee employee) {
		Employee emp=employeeservice.updataEmp(employee);
		return emp;
	}

步骤:
1、浏览器中查询1号信息http://localhost:8080/emp/1,此时已存入缓存中了,浏览器返回:
在这里插入图片描述
2、更新员工信息:http://localhost:8080/emp?id=1&lastName=zhaowu&gender=0
此时先执行注解的方法得到结果,再根据key值id=1找到数据库中的value,更新数据库同时更新缓存,再次查询时直接从缓存中查找不用再调用getEmp方法了。
在这里插入图片描述
注:如果参数中不规定key,则会用默认的以方法中的参数employee作为key,从而不会更新key=id=1的值(因为取数据的方法中key=id),因此需要规定要修改的key为id.

(2)@CacheEvict
缓存清除,清除在缓存中的该数据

  • 如果在@CacheEvict(cacheNames="emp",key="#id")中添加allEntries=true,则删除缓存emp中所有数据
  • @CacheEvict(cacheNames="emp",key="#id")中添加beforeInvocation=false 意思是缓存的清除是否在方法之前执行(默认为false,缓存清楚在执行方法后,如果在调用方法时出错,就不会清除缓存了)(true时,清除缓存在方法运行之前,无论方法运行是否出错都会清除缓存)
@CachePut(cacheNames="emp",key="#employee.id")
	public Employee updataEmp(Employee employee) {
		System.out.println("更新:"+employee);
		employeeMapper.updateEmp(employee);
		return employee;
	}
	
	@CacheEvict(cacheNames="emp",key="#id")
	public void deleteEmp(Integer id) {
		System.out.println("删除:"+id+"号员工");
		employeeMapper.deleteEmpById(id); //在数据库中删除该信息
	}

在控制类中添加:

@GetMapping("/delemp")
	public String deleteEmp(Integer id) {
		employeeservice.deleteEmp(id);
		return "sucess";
	}

步骤:
1、查询1号,再次查询时会从缓存中查找
2、删除1号:http://localhost:8080/delemp?id=1 ,此时数据库和缓存中已经都删除了该数据
3、再次查询1号时已经查找不到了
ps:该注解只会删除缓存中的数据,数据库中的数据删除是因为调用的方法deleteEmp中有employeeMapper.deleteEmpById(id)

(3)@Caching
包含了三种注解,可以指定复杂的缓存规则
在这里插入图片描述

@Caching(
			cacheable = {
					@Cacheable(value="emp",key="#lastName")
			},
			put = {  //根据员工的id和邮箱都能查找到员工
					@CachePut(value="emp",key="#result.id"),  //用返回结果的id作为key
					@CachePut(value="emp",key="#result.email"),
			}
	)
	public Employee getEmpByLastName(String lastName) {
		return employeeMapper.getEmpByLastName(lastName);
		
	}
@GetMapping("/emp/lastname/{lastname}")
	public Employee getEmployeeByLastName(@PathVariable("lastname") String lastName) {
		return employeeservice.getEmpByLastName(lastName);
	}

其中的@Cacheable实现了:可以按照员工的名字作为key进行查询,@CachePut实现了:同时以key=id和key=email的方式存入缓存中,下次再利用员工的id或者email进行查找该员工时,会直接从缓存中查找了,不用再查找数据库.
在这里插入图片描述
(4)@CacheConfig
在类前面添加此注解,可以进行配置,可以指定缓存的名字:emp,缓存的keyGenerator,CacheManager等公共的属性,在类里面的方法中就不需要写value="emp"
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值