(一)、Spring缓存抽象
Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们的开发。
1)、几个重要概念&缓存注解
image.png(二)、基础环境的搭建
-
创建项目
[图片上传失败...(image-fa9ada-1565021029707)]
- 导入数据库文件
```sql
/*
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;
```
3. 创建javabean
![在这里插入图片描述](https://img.haomeiwen.com/i18288748/74e1f92b3ce08199.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
4. 整合Mybatis操作数据库
```yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/spring_cache?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
username: root
password: XHHP0913
driver-class-name: com.mysql.jdbc.Driver
mybatis:
mapper-locations: classpath:mybatis/mapper/*.xml
configuration:
map-underscore-to-camel-case: true
```
```java
package com.crud.springboot.mapper;
import com.crud.springboot.bean.Employee;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface EmployeeMapper {
public Employee getEmpById(Integer id);
public void updateEmp(Employee employee);
public void deleteEmpById(Integer id);
public void insertUser(Employee employee);
public Employee getEmpByLastName(String lastName);
}
```
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.crud.springboot.mapper.EmployeeMapper">
<select id="getEmpById" resultType="com.crud.springboot.bean.Employee">
select * from employee where id=#{id}
</select>
<update id="updateEmp">
update employee set lastName=#{lastName},email=#{email},gender=#{gender},d_id=#{dId} where id=#{id}
</update>
<delete id="deleteEmpById">
delete from employee where id=#{id}
</delete>
<insert id="insertUser">
insert into employee(lastName,email,gender,d_id) values(#{lastName},#{email},#{gender},,#{dId})
</insert>
<select id="getEmpByLastName" resultType="com.crud.springboot.bean.Employee">
select * from employee where lastName=#{lastName}
</select>
</mapper>
```
-
搭建简单的Service层和Controller层
在这里插入图片描述
(三)、快速体验缓存(@Cacheable)
- 开启基于注解的缓存
```java
@MapperScan("com.crud.springboot.mapper")
@SpringBootApplication
@EnableCaching
public class Springboot01CacheApplication
```
-
标注缓存注解即可
@Cacheable(cacheNames = {"emp","temp"}) public Employee getEmp(Integer id){ System.out.println("查询2号员工"); Employee emp = employeeMapper.getEmpById(id); return emp; }
备注:
1. 将方法的运行结果进行缓存;以后再要相同的数据,直接从缓存中获取,不用调用方法;
2.CacheMananger管理多个Cache组件,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件都有自己唯一一个名字
3.@Cacheable有如下几个可配置属性
属性 | 配置内容 |
---|---|
cacheNames/value | 指定缓存的名字 |
key | 缓存数据时使用的key;可以用它来指定。默认是使用方法参数的值 |
keyGenerator | key的生成器:可以自己指定key的生成器的组件id |
cacheMananger | 指定缓存管理器;或者cacheResolver指定缓存解析器 |
condition | 指定符合条件的情况下才缓存 |
unless | 否定缓存;当unless指定的条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断 |
sync | 是否使用异步模式 |
备注 | ==key/keyGenerator 二选一使用、cacheMananger/cacheResolver 二选一使用== |
4.支持SPEL表达式
在这里插入图片描述
(四)、@CachePut
@CachePut(value = {"emp"},key = "#result.id")
public Employee updateEmp(Employee employee){
System.out.println("update"+employee);
employeeMapper.updateEmp(employee);
return employee;
}
- @CachePut:既调用方法,又更新缓存数据;
- 修改数据库的某个数据,同时更新缓存
- 运行时机:
1、先调用目标方法
2、将目标方法的结果缓存起来 - 测试样例:
1、首先查询一号员工,查询数据库,会把结果放在缓存中
2、再次查询一号员工,直接从缓存中获取结果
3、跟新一号员工的数据,调用updateEmp的方法,会将更新的结果放入缓存中
4、再次查询一号员工,发现结果更新了,但是并未查询数据库
注意:@CachePut的key要和@Cacheable的key相同才能更新缓存
(五)、@CacheEvict
/**
* @CacheEvict 清除缓存
* 可以使用属性 allEntries = true 清空所有的缓存
* 可以使用属性 beforeInvocation = true 在方法执行之前清空缓存(默认是false)
* @param id
*/
@CacheEvict(cacheNames = {"emp"},key = "#id")
public void deleteEmp(Integer id){
System.out.println("deleteEmp:"+id);
employeeMapper.deleteEmpById(id);
}
- @CacheEvict:用于清空缓存
- 修改数据库的某个数据,同时更新缓存
- 运行时机:
1、用于删除数据时清空缓存; - 测试样例:
1、首先查询一号员工,查询数据库,会把结果放在缓存中
2、再次查询一号员工,直接从缓存中获取结果
3、删除一号员工
4、再次查询一号员工,发现需要再次查询数据库才能获得结果
注意:1、可以使用属性 allEntries = true 清空所有的缓存
2、可以使用属性 beforeInvocation = true 在方法执行之前清空缓存(默认false)
(六)、@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){
Employee emp = employeeMapper.getEmpByLastName(lastName);
return emp;
}
- @Caching:是一个组合注解,可以组合@Cacheable、@CachePut、@CacheEvict
(七)、@CacheConfig
@Service
@CacheConfig(cacheNames = "emp")
public class EmployeeService {
- @CacheConfig:抽取缓存的公共配置
(八)、整合Redis作为缓存
SpringBoot默认使用的是ConcurrentMapCacheManager=ConcurrentMapCache;
开发中使用缓存中间件:redis、memcached、ehcache;
(1)、在虚拟机上搭建redis环境
(2)、引入SpringBoot的Redis starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
(3)、配置Redis
spring:
datasource:
redis:
host: 192.168.56.102
(4)、测试缓存
原理: CacheManager==Cache 缓存组件来实际给缓存中存取数据
-
引入redis的starter,容器中保存的是RedisCacheManager
-
RedisCacheManager 帮我们创建RedisCache来作为缓存组件
-
RedisCache通过操作Redis来缓存数据
-
默认保存数据 k-v 都是通过Object;利用序列化保存;如何保存为json:
1、引入了redis的starter,cacheManager变为RedisCacheManager;
2、默认创建的CacheManager操作Redis的时候使用的是RedisTemplate<Object, Object>(默认使用jdk的序列化机制) -
自定义CacheManager:
/** * 2.0版本RedisCacheManager * @param redisConnectionFactory * @param empRedisTemplate * @return */ @Bean public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory){ //初始化一个RedisCacheWriter RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory); //设置CacheManager的值序列化方式为json序列化 RedisSerializer<Employee> jsonSerializer = new Jackson2JsonRedisSerializer<Employee>(Employee.class); RedisSerializationContext.SerializationPair<Employee> pair = RedisSerializationContext.SerializationPair .fromSerializer(jsonSerializer); RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(pair); //初始化RedisCacheManager return new RedisCacheManager(redisCacheWriter, defaultCacheConfig); }
(5)、指定统一的CacheManager
如果不指定不同的CacheManager会导致混乱,出现错误
在这里插入图片描述
需要为不同方法指定对应的CacheManager,并且要选择一个Primary的CacheManager
@Primary
@Bean
public RedisCacheManager deptRedisCacheManager(RedisConnectionFactory redisConnectionFactory){
//初始化一个RedisCacheWriter
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
//设置CacheManager的值序列化方式为json序列化
RedisSerializer<Department> jsonSerializer = new Jackson2JsonRedisSerializer<Department>(Department.class);
RedisSerializationContext.SerializationPair<Department> pair = RedisSerializationContext.SerializationPair
.fromSerializer(jsonSerializer);
RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(pair);
//初始化RedisCacheManager
return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
}
@Cacheable(cacheNames = "dept",cacheManager = "deptRedisCacheManager")
public Department getDeptById(Integer id){
Department department=departmentMapper.getDeptById(id);
return department;
}
(6)、如需在编码中使用缓存
@Qualifier("deptCacheManager")
RedisCacheManager deptCacheManager;
//@Cacheable(cacheNames = "dept",cacheManager = "deptRedisCacheManager")
public Department getDeptById(Integer id){
Department department=departmentMapper.getDeptById(id);
//获取某个缓存
Cache dept = deptCacheManager.getCache("dept");
dept.put("dept",department);
return department;
}
网友评论