一、简介
1、缓存介绍
Spring 从 3.1 开始就引入了对 Cache 的支持。定义了 org.springframework.cache.Cache 和 org.springframework.cache.CacheManager 接口来统一不同的缓存技术。并支持使用 JCache(JSR-107)注解简化我们的开发。
其使用方法和原理都类似于 Spring 对事务管理的支持。Spring Cache 是作用在方法上的,其核心思想是,当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值对存在缓存中。
2、Cache 和 CacheManager 接口说明
Cache 接口包含缓存的各种操作集合,你操作缓存就是通过这个接口来操作的。
Cache 接口下 Spring 提供了各种 xxxCache 的实现,比如:RedisCache、EhCache、ConcurrentMapCache
CacheManager 定义了创建、配置、获取、管理和控制多个唯一命名的 Cache。这些 Cache 存在于 CacheManager 的上下文中。
小结:
每次调用需要缓存功能的方法时,Spring 会检查指定参数的指定目标方法是否已经被调用过,如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。
二、@Cacheable 注解使用详细介绍
@Cacheable 这个注解,用它就是为了使用缓存的。所以我们可以先说一下缓存的使用步骤:
1、缓存使用步骤
1、开启基于注解的缓存,使用 @EnableCaching 标识在 SpringBoot 的主启动类上。
2、标注缓存注解即可
① 第一步:开启基于注解的缓存,使用 @EnableCaching 标注在 springboot 主启动类上
@EnableSwagger2
@EnableScheduling
@EnableFeignClients(basePackages = {"src.main.biz.smallProject.client","com.codingapi.tx"})
@EnableJpaRepositories(basePackages = {"src.main.biz.smallProject.web.*.dao","src.main.newgrand.framework.common.dao" })
@EntityScan(basePackages = { "src.main.biz.smallProject.web.*.entity","src.main.newgrand.framework.common.domain"})
@EnableCaching
public class StartApp {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(StartApp.class);
new SpringUtil().setApplicationContext(context);
}
}
② 第二步:标注缓存注解
package src.main.biz.smallProject.web.cost.service.impl;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.QueryResults;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import src.main.biz.smallProject.utils.CollectionUtils;
import src.main.biz.smallProject.web.construct.form.IdsForm;
import src.main.biz.smallProject.web.cost.dao.BusinessScopeJPA;
import src.main.biz.smallProject.web.cost.entity.BusinessScopeEntity;
import src.main.biz.smallProject.web.cost.entity.QBusinessScopeEntity;
import src.main.biz.smallProject.web.cost.form.BusinessScopeForm;
import src.main.biz.smallProject.web.cost.form.BusinessScopeQueryForm;
import src.main.biz.smallProject.web.cost.service.BusinessScopeService;
import src.main.biz.smallProject.web.cost.vo.BusinessScopeVO;
import src.main.newgrand.framework.common.constants.PageData;
import src.main.newgrand.framework.common.exception.BusinessException;
import src.main.newgrand.framework.common.service.impl.BaseServiceImpl;
import src.main.newgrand.framework.common.utils.ResultRes;
import java.time.LocalDateTime;
import java.util.List;
import static src.main.biz.smallProject.redis.CacheConstants.BUSINESS_SCOPE_CACHE;
/**
* @Description
* @Author yql
* @Date 2022-05-10 10:44:29 */
@Service
public class BusinessScopeServiceImpl extends BaseServiceImpl<BusinessScopeJPA, BusinessScopeEntity, BusinessScopeVO> implements BusinessScopeService {
@Autowired
private JPAQueryFactory jpaQueryFactory;
QBusinessScopeEntity qBusinessScopeEntity = QBusinessScopeEntity.businessScopeEntity;
@Autowired
private BusinessScopeService businessScopeService;
@Override
@Transactional(rollbackFor = BusinessException.class)
@CacheEvict(cacheNames = {BUSINESS_SCOPE_CACHE}, allEntries = true)
public ResultRes add(BusinessScopeForm form) {
BusinessScopeEntity businessScopeEntity = new BusinessScopeEntity();
BeanUtils.copyProperties(form,businessScopeEntity);
businessScopeService.save(businessScopeEntity);
return ResultRes.success();
}
@Override
@Transactional(rollbackFor = BusinessException.class)
@CacheEvict(cacheNames = {BUSINESS_SCOPE_CACHE}, allEntries = true)
public ResultRes update(BusinessScopeForm form) {
BusinessScopeEntity businessScopeEntity = findById(form.getPhid());
if(businessScopeEntity == null){
throw new BusinessException("数据不存在,请检查!");
}
BeanUtils.copyProperties(form,businessScopeEntity);
businessScopeService.updateSelectiveById(businessScopeEntity);
return ResultRes.success();
}
@Override
@Cacheable(cacheNames = BUSINESS_SCOPE_CACHE, key = "{ " + "#root.methodName, #form.status}", unless = "#result == null")
public ResultRes<PageData> list(BusinessScopeQueryForm form) {
long currPage = form.getCurrent();
long size = form.getSize();
BooleanBuilder booleanBuilder = new BooleanBuilder();
booleanBuilder.and(qBusinessScopeEntity.phDelflag.eq(0L));
if(form.getStatus() != null){
booleanBuilder.and(qBusinessScopeEntity.status.eq(form.getStatus()));
}
QueryResults<BusinessScopeVO> results = jpaQueryFactory
.select(Projections.bean(
BusinessScopeVO.class,
qBusinessScopeEntity.phid,
qBusinessScopeEntity.name,
qBusinessScopeEntity.status))
.from(qBusinessScopeEntity)
.where(booleanBuilder)
.orderBy(qBusinessScopeEntity.phInsertDt.desc())
.offset((currPage - 1) * size)
.limit(size).fetchResults();
PageData pageData = new PageData(results.getResults(), results.getTotal(), size, currPage);
return ResultRes.success(pageData);
}
@Override
@Transactional(rollbackFor = BusinessException.class)
@CacheEvict(cacheNames = {BUSINESS_SCOPE_CACHE}, allEntries = true)
public ResultRes deleteData(IdsForm form) {
List<Long> ids = form.getIds();
List<BusinessScopeEntity> businessScopeEntityList = queryFactory.selectFrom(qBusinessScopeEntity)
.where(qBusinessScopeEntity.phid.in(ids).and(qBusinessScopeEntity.phDelflag.eq(0L)))
.fetch();
if(CollectionUtils.isEmpty(businessScopeEntityList)){
throw new BusinessException("数据不存在,请检查!");
}
queryFactory.update(qBusinessScopeEntity)
.set(qBusinessScopeEntity.phDelflag, 1L)
.set(qBusinessScopeEntity.phUpdateDt, LocalDateTime.now())
.where(qBusinessScopeEntity.phid.in(ids))
.execute();
return ResultRes.success();
}
}
2、常用属性说明
cacheNames/value :用来指定缓存组件的名字
key :缓存数据时使用的 key,可以用它来指定。默认是使用方法参数的值。(这个 key 你可以使用 spEL 表达式来编写)
keyGenerator :key 的生成器。 key 和 keyGenerator 二选一使用
cacheManager :可以用来指定缓存管理器。从哪个缓存管理器里面获取缓存。
condition :可以用来指定符合条件的情况下才缓存
unless :否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。当然你也可以获取到结果进行判断。(通过 #result 获取方法结果)
sync :是否使用异步模式。
3、spEL 编写 key
前面说过,缓存的 key 支持使用 spEL 表达式去编写,下面总结一下使用 spEL 去编写 key 可以用的一些元数据: