Spring Cache
1、简介
Spring 从 3.1 开始定义了 org.springframework.cache.Cache 和
org.springframework.cache.CacheManager 接口来统一不同的缓存技术; 并支持使用
JCache(JSR-107)注解简化我们开发;
Cache 接口为缓存的组件规范定义,包含缓存的各种操作集合; Cache 接 口 下 Spring 提 供 了 各 种 xxxCache 的 实 现 ; 如 RedisCache , EhCacheCache , ConcurrentMapCache 等;
每次调用需要缓存功能的方法时,Spring 会检查检查指定参数的指定的目标方法是否已
经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓 存结果后返回给用户。下次调用直接从缓存中获取。
使用 Spring 缓存抽象时我们需要关注以下两点;
1、确定方法需要被缓存以及他们的缓存策略
2、从缓存中读取之前缓存存储的数据
2、基础概念
3、注解
@Cacheable:触发数据保存到缓存。
@CacheEvict:触发删除缓存逐出。
@CachePut:在不影响方法执行的更新缓存。
@Caching:组合以上多个缓存操作。
@CacheConfig:在类级别 共享缓存相关配置。
4、表达式语法
5、缓存穿透问题解决
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
允许 null 值缓存
6.缓存使用
6.1 @Cacheable注解的使用
//1.获取一级分类,
//每一个需要缓存的数据,都指定放到指定名称的缓存[缓存分区(按业务类型)]
@Cacheable({"category"})
//@Cacheable,开启缓存。代表此方法的结果需要缓存。
//若缓存有,方法不调用,否则调用方法,将方法结果放入缓存
@Override
public List<CategoryEntity> getLevelOneCategorys() {
System.out.println("getLevelOneCategorys...获取一级分类数据");
QueryWrapper<CategoryEntity> wrapper = new QueryWrapper<CategoryEntity>().eq("parent_cid", 0);
List<CategoryEntity> entities = baseMapper.selectList(wrapper);
return entities;
}
1). @Cacheable注解的细节
1.每一个需要缓存的数据,都指定放到指定那个名称的缓存[缓存分区(按业务类型)]
2. @Cacheable({"category"}),//开启缓存。
代表此方法的结果需要缓存。若缓存中有,方法不调用;否则调用方法,最后将方法结果放入缓存
3. 默认行为
1).若缓存中有,方法不调用
2).key默认自带生成:缓存的名称::SimpleKey[],(自动生成key值)
3).缓存的value值,默认使用jdk序列化机制,将序列化的数据存入redis
4).默认过期时间(ttl):-1 ,代表永不过期
自定义缓存配置
自定义设置
1).指定生成缓存生成的key。key属性指定,接受一个spel,spel 详细介绍
2).指定缓存数据存活时间。
配置文件修改ttl:spring.cache.redis.time-to-live=3600000
3).将数据保存为json格式
1.a).设置缓存key、value(缓存名称)
1.b).设置过期时间 、数据转为json格式(为了缓存数据为json)
配置文件缓存配置(application.properties)
#配置redis
spring.redis.host=192.168.56.10
spring.redis.port=6379
#配置Spring Cache
spring.cache.type=redis
#spring.cache.cache-names=qq
#设置过期时间为1小时,单位:毫秒 ***
spring.cache.redis.time-to-live=3600000
#指定缓存名字前缀就用,不指定就用缓存名称作为前缀
spring.cache.redis.key-prefix=CACHE_
spring.cache.redis.use-key-prefix=true
#是否缓存空值null,防止缓存穿透
spring.cache.redis.cache-null-values=true
缓存配置类
@EnableCaching
@Configuration
@EnableConfigurationProperties(CacheProperties.class)
public class MyCacheConfig {
@Bean
RedisCacheConfiguration cacheConfiguration(CacheProperties cacheProperties) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
//设置缓存key
config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
//设置缓存的值
config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
CacheProperties.Redis redisProperties = cacheProperties.getRedis();
//将配置文件的所有配置生效
if (redisProperties.getTimeToLive() != null) {
//设置ttl(过期时间)
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixKeysWith(redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
问题:配置文件的配置没有生效,介绍如下,代码如上(缓存配置类)
/**
* 配置文件的东西没有用上:如ttl(过期时间)
* 1.原来和配置文件绑定的配置类是:
* @ConfigurationProperties(prefix = “spring.cache”)
* public class CacheProperties
* 2.要让他生效,@EnableConfigurationProperties(CacheProperties.class)
* 法1:注入
* @Autowired CacheProperties cacheProperties
* 法2:方法参数 ***
* RedisCacheConfiguration cacheConfiguration(CacheProperties cacheProperties){
*/
测试:http://localhost:10000/#
2).缓存删除 @CacheEvict
逻辑:修改数据、删除缓存----失效模式
@CacheEvict:失效模式(修改数据,删除缓存,下一次查询数据库,把数据添加到缓存)
指定缓存名称value、key
@CacheEvict(value = {“category”},key = “getLevelOneCategorys”)
代码
//级联更新所有关联数据
@Override
@CacheEvict(value = {"category"},key = "'getLevelOneCategorys'")
public void updateDetail(CategoryEntity category) {
//更新分类表
this.updateById(category);
//保证冗余字段数据一致
if (!StringUtils.isEmpty(category.getName())) {
//同步更新其他关联表的数据
categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());
// TODO 更新其他表关联
}
}
问题:
“message”: “EL1008E: Property or field ‘selectIndexList’ cannot be found on object of type ‘org.springframework.cache.interceptor.CacheExpressionRootObject’ - maybe not public or not valid?”
解决:
测试
修改分类数据,缓存会删除
删除category指定缓存
@CacheEvict(value = {“category”},key = “getLevelOneCategorys”) ,
删除category下的所有缓存删除
@CacheEvict(value = {“category”},allEntries = true)
3).多个组合注解 @Caching
修改获得json数据
evict表示清除缓存
@Caching 同时进行多个缓存操作
删除多个指定名称下指定缓存
@Caching(evict = {
@CacheEvict(value = {“category”}, key = “‘getLevelOneCategorys’”),
@CacheEvict(value = {“category”}, key = “‘getCatalogJson’”)
})
测试
3.1).gulimall.com
3.2). 修改分类数据,缓存会删除
4). @CachePut 双写模式
注解说明
1.@CacheEvict:失效模式(修改数据,删除缓存,下一次查询数据库,把数据添加到缓存)
指定缓存分区名value、key
@CacheEvict(value = {“category”},key = “getLevelOneCategorys”) , 删除category指定缓存
2.@CacheEvict(value = {“category”},allEntries = true) ,删除category下的所有缓存
3. @Caching 同时进行多个缓存操作
4.存储统一类型的数据,放在指定统一分区,方便删除库存(allEntries = true)
@CachePut//双写模式