学习尚硅谷做的笔记,推荐大家学习
Java Caching
定义了5个核心接口,分别是CachingProvider
, CacheManager
, Cache
, Entry
和 Expiry
。
Spring从3.1开始定义了org.springframework.cache.Cache 和org.springframework.cache.CacheManager接口来统一不同的缓存技术; 并支持使用JCache(JSR-107)注解简化我们开发;
Cache | 缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、ConcurrentMapCache等 |
---|---|
CacheManager | 缓存管理器,管理各种缓存(Cache)组件 |
@Cacheable | 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 |
@CacheEvict | 清空缓存 |
@CachePut | 保证方法被调用,又希望结果被缓存。 |
@EnableCaching | 开启基于注解的缓存 |
keyGenerator | 缓存数据时key生成策略 |
serialize | 缓存数据时value序列化策略 |
/*
Navicat MySQL Data Transfer
Source Server : 本地
Source Server Version : 50528
Source Host : 127.0.0.1:3306
Source Database : springboot_cache
Target Server Type : MYSQL
Target Server Version : 50528
File Encoding : 65001
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for department
-- ----------------------------
DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`departmentName` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for employee
-- ----------------------------
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`lastName` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`gender` int(2) DEFAULT NULL,
`d_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
使用SpringBoot初始化器选择依赖是最好的方式
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
spring:
datasource:
url: jdbc:mysql://172.16.0.192:3306/springboot_cache
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
#开启驼峰命名 (did不显示)
mybatis:
configuration:
map-underscore-to-camel-case: true
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Department implements Serializable {
private Integer id;
private String departmentName;
}
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Employee implements Serializable {
private Integer id;
private String lastName;
private String email;
private Integer gender; //性别 1男 0女
private Integer dId;
}
@Mapper
public interface DepartmentMapper {
@Select("SELECT * FROM department WHERE id = #{id}")
Department getDeptById(Integer id);
}
@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);
}
@Service
public class DeptService {
@Autowired
DepartmentMapper departmentMapper;
public Department getDeptById(Integer id) {
System.out.println("查询部门" + id);
Department department = departmentMapper.getDeptById(id);
return department;
}
public Department getDeptByIdManager(Integer id) {
System.out.println("查询部门" + id);
Department department = departmentMapper.getDeptById(id);
return department;
}
}
@Service
public class EmployeeService {
@Autowired
EmployeeMapper employeeMapper;
public Employee getEmp(Integer id) {
System.out.println("查询" + id + "号员工");
Employee emp = employeeMapper.getEmpById(id);
return emp;
}
public Employee updateEmp(Employee employee) {
System.out.println("updateEmp:" + employee);
employeeMapper.updateEmp(employee);
return employee;
}
public void deleteEmp(Integer id) {
System.out.println("deleteEmp:" + id);
employeeMapper.deleteEmpById(id);
int i = 10 / 0;
}
public Employee getEmpByLastName(String lastName) {
return employeeMapper.getEmpByLastName(lastName);
}
}
@RestController
public class DeptController {
@Autowired
DeptService deptService;
@GetMapping("/dept/{id}")
public Department getDept(@PathVariable("id") Integer id) {
return deptService.getDeptById(id);
}
@GetMapping("/depts/{id}")
public Department getDepts(@PathVariable("id") Integer id) {
return deptService.getDeptByIdManager(id);
}
}
@RestController
public class EmployeeController {
@Autowired
EmployeeService employeeService;
@GetMapping("/emp/{id}")
public Employee getEmployee(@PathVariable("id") Integer id) {
Employee employee = employeeService.getEmp(id);
return employee;
}
@GetMapping("/emp")
public Employee update(Employee employee) {
Employee emp = employeeService.updateEmp(employee);
return emp;
}
@GetMapping("/delemp")
public String deleteEmp(Integer id) {
employeeService.deleteEmp(id);
return "success";
}
@GetMapping("/emp/lastname/{lastName}")
public Employee getEmpByLastName(@PathVariable("lastName") String lastName) {
return employeeService.getEmpByLastName(lastName);
}
}
@MapperScan("cn.zysheep.springboot.mapper")
@SpringBootApplication
@EnableCaching //开启基于注解的缓存
public class Springboot08CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot08CacheApplication.class, args);
}
}
首页发送请求
BUG:did
不显示,在yml里面开启驼峰命名法
后台显示从数据库中查数据
在启动类中开启注解缓存
@MapperScan("cn.zysheep.springboot.mapper")
@SpringBootApplication
@EnableCaching //开启基于注解的缓存
public class Springboot08CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot08CacheApplication.class, args);
}
}
@Cacheable标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存, 如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。
public @interface Cacheable {
@AliasFor("cacheNames")
String[] value() default {};
@AliasFor("value")
String[] cacheNames() default {};
#指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存;
String key() default "";
# 缓存数据使用的key;可以用它来指定。默认是使用方法参数的值.如(1-方法返回值)
编写SpEL; #id;参数id的值 #a0 #p0 #root.args[0]
String keyGenerator() default "";
# key的生成器;可以自己指定key的生成器的组件id,key/keyGenerator:二选一使用;
String cacheManager() default "";
# 指定缓存管理器;或者cacheResolver指定获取解析器
String cacheResolver() default "";
# 缓存解析器
String condition() default "";
# 指定符合条件的情况下才缓存;condition = "#id>0" condition = "#a0>1":第一个参数的值>1的时候才进行缓存
String unless() default ""; # 否定缓存;当unless指定的条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断
boolean sync() default false; # 是否使用异步模式
}
属性key用SpEL的写法
名字 | 位置 | 描述 | 示例 |
---|---|---|---|
methodName | root object | 当前被调用的方法名 | #root.methodName |
method | root object | 当前被调用的方法 | #root.method.name |
target | root object | 当前被调用的目标对象 | #root.target |
targetClass | root object | 当前被调用的目标对象类 | #root.targetClass |
args | root object | 当前被调用的方法的参数列表 | #root.args[0] |
caches | root object | 当前方法调用使用的缓存列表(如@Cacheable(value={“cache1”, “cache2”})),则有两个cache | #root.caches[0].name |
argument name | evaluation context | 方法参数的名字. 可以直接 #参数名 ,也可以使用 #p0或#a0 的形式,0代表参数的索引; | #iban 、 #a0 、 #p0 |
result | evaluation context | 方法执行后的返回值(仅当方法执行之后的判断有效,如‘unless’,’cache put’的表达式 ’cache evict’的表达式beforeInvocation=false) | #result |
1)在方法上添加缓存注解
@Cacheable(value = {"emp"})
public Employee getEmp(Integer id) {
System.out.println("查询" + id + "号员工");
Employee emp = employeeMapper.getEmpById(id);
return emp;
}
页面访问,首次查询去数据库中看,后台有打印,第二次查同样的数据,会查缓存,后台没有打印
1、自动配置类;CacheAutoConfiguration
2、缓存的配置类
3、哪个配置类默认生效:SimpleCacheConfiguration
;
4、给容器中注册了一个CacheManager
:ConcurrentMapCacheManager
5、可以获取和创建ConcurrentMapCache
类型的缓存组件;他的作用将数据保存在ConcurrentMap
中;
@CachePut也可以声明一个方法支持缓存功能。与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。
@CachePut:既调用方法,又更新缓存数据;同步更新缓存 修改了数据库的某个数据,同时更新缓存;
运行时机:
@CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作
@CacheEvict(value = "emp", beforeInvocation = true/*key = "#id",*/)
public void deleteEmp(Integer id) {
System.out.println("deleteEmp:" + id);
employeeMapper.deleteEmpById(id);
int i = 10 / 0;
}
添加缓存注解
@CachePut(value = "emp")
public Employee updateEmp(Employee employee) {
System.out.println("updateEmp:" + employee);
employeeMapper.updateEmp(employee);
return employee;
}
结果:总是查询一号员工,修改的数据没有查询到
为什么?
因为缓存默认的key
为参数,即第一次查询到的emp
缓存的key 为id
;更新员工将方法的返回值放进缓存了;key是传入的employee对象, 值是返回的employee对象;是两个不同的缓存,所以总是显示为一号员工
如何解决
我们只需要缓存相同的key就可以实现修改缓存数据;即取缓存的key和存缓存的key相同key = "#result.id"
,key = "#employee.id"
key = "#employee.id":使用传入的参数的员工id;
key = "#result.id":使用返回后的id
注意:@Cacheable的key是不能用#result
为什么是没更新前的?【1号员工没有在缓存中更新】
@CachePut(value = "emp", key = "#result.id")
public Employee updateEmp(Employee employee) {
System.out.println("updateEmp:" + employee);
employeeMapper.updateEmp(employee);
return employee;
}
@Caching
定义复杂的缓存规则
@Caching(
cacheable = {
@Cacheable(value="emp",key = "#lastName")
},
put = {
@CachePut(value="emp",key = "#result.id"),
@CachePut(value="emp",key = "#result.email")
}
)
public Employee getEmpByLastName(String lastName) {
return employeeMapper.getEmpByLastName(lastName);
}
每次都要定义cacheNames ,和key,可以在类上定义公共配置,方便管理
@CacheConfig(cacheNames = "emp"/*,cacheManager = "employeeCacheManager"*/) //抽取缓存的公共配置
默认使用的是ConcurrentMapCacheManager==ConcurrentMapCache;将数据保存在 ConcurrentMap
中开发中使用缓存中间件;redis、memcached、ehcache;