前言
日常開發中,快取是解決資料庫壓力的一種方案,通常用於頻繁查詢的資料,例如新聞中的熱點新聞,本文記錄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