前言
日常開發中,緩存是解決數據庫壓力的一種方案,通常用於頻繁查詢的數據,例如新聞中的熱點新聞,本文記錄springboot中使用cache緩存。
官方文檔介紹:https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#boot-features-caching-provider-generic
工程結構

代碼編寫
pom引入依賴,引入cache緩存,數據庫使用mysql,ORM框架用jpa
<!--添加springdata-cache依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- 引入ehcache支持 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<!--添加springdata-jpa依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--添加MySQL驅動依賴 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
配置文件
server.port=10010 spring.application.name=springboot-cache spring.cache.type=ehcache spring.cache.ehcache.config=classpath:/ehcache.xml
ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false">
<!-- 磁盤緩存位置 -->
<diskStore path="java.io.tmpdir"/>
<!-- maxEntriesLocalHeap:堆內存中最大緩存對象數,0沒有限制 -->
<!-- maxElementsInMemory: 在內存中緩存的element的最大數目。-->
<!-- eternal:elements是否永久有效,如果為true,timeouts將被忽略,element將永不過期 -->
<!-- timeToIdleSeconds:發呆秒數,發呆期間未訪問緩存立即過期,當eternal為false時,這個屬性才有效,0為不限制 -->
<!-- timeToLiveSeconds:總存活秒數,當eternal為false時,這個屬性才有效,0為不限制 -->
<!-- overflowToDisk: 如果內存中數據超過內存限制,是否要緩存到磁盤上 -->
<!-- statistics:是否收集統計信息。如果需要監控緩存使用情況,應該打開這個選項。默認為關閉(統計會影響性能)。設置statistics="true"開啟統計 -->
<!--
默認緩存
無過期時間,但 600 秒內無人訪問緩存立即過期
-->
<defaultCache
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="600"
timeToLiveSeconds="0"
overflowToDisk="false">
</defaultCache>
<!--
xx業務緩存
在有效的 120 秒內,如果連續 60 秒未訪問緩存,則緩存失效。
就算有訪問,也只會存活 120 秒。
-->
<cache name="myCache"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="0"
overflowToDisk="false">
</cache>
</ehcache>
先寫一個套tb_user表的CRUD代碼

@RestController @RequestMapping("/tbUser/") public class TbUserController { @Autowired private TbUserService tbUserService; //方便測試暫時改成GetMapping @GetMapping("list") // @PostMapping("list") public List<TbUser> list(TbUser entityVo) { return tbUserService.list(entityVo); } @GetMapping("get/{id}") public TbUser get(@PathVariable("id")Integer id) { return tbUserService.get(id); } //方便測試暫時改成GetMapping @GetMapping("save") // @PostMapping("save") public TbUser save(TbUser entityVo) { return tbUserService.save(entityVo); } @GetMapping("delete/{id}") public Integer delete( @PathVariable("id") Integer id) { return tbUserService.delete(id); } }
opjo實體類要實現序列化
@Entity @Table(name = "tb_user") @Data public class TbUser implements Serializable { @Id @GeneratedValue(strategy= GenerationType.IDENTITY) private Integer id;//表id private String username;//用戶名 private String password;//密碼 private Date created;//創建時間 private Integer descriptionId;//關聯詳情id }
serviceImpl中,使用注解來開啟緩存
@Service @Transactional @CacheConfig(cacheNames = {"myCache"}) public class TbUserServiceImpl implements TbUserService{ @PersistenceContext private EntityManager em; @Autowired private TbUserRepository tbUserRepository; //@Cacheable緩存數據:key為userList,value為返回值List<TbUser> @Cacheable(key = "'userList'") @Override public List<TbUser> list(TbUser entityVo) { System.out.println("獲取list用戶列表緩存數據,"+new Date()); return tbUserRepository.findAll(Example.of(entityVo)); } //@Cacheable緩存數據:key為參數id,value為返回值TbUser @Cacheable(key = "#id") @Override public TbUser get(Integer id) { System.out.println("獲取數據緩存,key:"+id); Optional<TbUser> optionalE = tbUserRepository.findById(id); if (!optionalE.isPresent()) { throw new RuntimeException("ID不存在!"); } return optionalE.get(); } //@CachePut緩存新增的或更新的數據到緩存,其中緩存的名稱為people,數據的key是person的id @CachePut(key = "#entityVo.id") // @CacheEvict從緩存中刪除key為參數userList的數據 @CacheEvict(key = "'userList'") @Override public TbUser save(TbUser entityVo) { System.out.println("新增/更新緩存,key:"+entityVo.getId()); //entityVo傳啥存啥,會全部更新 return tbUserRepository.save(entityVo); } //清空所有緩存 @CacheEvict(allEntries=true) @Override public Integer delete(Integer id) { System.out.println("清空所有緩存"); tbUserRepository.deleteById(id); return id; } }
效果演示
http://localhost:10010/tbUser/save?id=2&username=李四
調用save方法,key為2,value為當前tbUser對象的數據被緩存下來


http://localhost:10010/tbUser/get/2
當我們調用get方法時,直接獲取緩存數據,控制台啥也不打印,連serviceImpl的get方法都不進去(可以打斷點調試)


http://localhost:10010/tbUser/save?id=2&username=王五
當我們再次調用save方法更新username時,緩存數據也被更新


http://localhost:10010/tbUser/get/2
再次調用get接口,直接返回緩存數據,后台也是方法都不進去,啥也不打印


http://localhost:10010/tbUser/delete/2
調用delete接口,刪除數據,同時刪除緩存


再次調用get接口,發現緩存數據被清除,查詢數據庫

http://localhost:10010/tbUser/list
首次調用list接口,key為userList的,value為用戶集合數據被緩存下來,再次調用直接返回緩存數據


當調用save接口,數據更新,刪除key為userList的緩存,再次調用list時,重新查庫並設置緩存

我們配置了緩存發呆時間,當120秒內未使用該緩存,立即過期,一直用就會一直存在
我們先同時訪問兩個接口list、get,list接口2分鍾后再次訪問,get接口不能超過2分鍾是不是訪問一下,結果如預期

PS:原先使用了這個jar包,有報錯
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.8.1</version>
</dependency>
后面改成用上面“代碼編寫”里pom中引的jnet.sf.ehcache下面的ar
后記
緩存除了能緩解數據庫壓力,還能做用戶登錄狀態控制,例如:用戶登錄成功后cookie中保存頒發的token令牌設置永不過期,緩存存活時間也設置永不過期,發呆時間設置1天,這樣只有用戶在1天內有訪問緩存接口,那他就可以一直保留登錄狀態,直至有其他業務將token或者緩存清掉。
springboot使用cache緩存暫時先記錄到這,后續有空再進行補充。
代碼開源
代碼已經開源、托管到我的GitHub、碼雲:
GitHub:https://github.com/huanzi-qch/springBoot
碼雲:https://gitee.com/huanzi-qch/springBoot
