一、簡介
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 可以用的一些元數據: