SpringBoot中cache使用的實(shí)現(xiàn)示例
前言
對于SpringBoot的Cache,其實(shí)已經(jīng)有很多文章對使用說明、底層詳解等各個(gè)角度進(jìn)行了講解。
本文則會(huì)結(jié)合更加詳細(xì)的使用場景,對cache的存儲(chǔ)和查詢邏輯進(jìn)行說明。
緩存的增刪改查
查詢數(shù)據(jù)列表
先看一段代碼
@Cacheable(value = "devices")
public Set<Integer> getAllDeviceId() {
LambdaQueryWrapper<DevicePo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DevicePo::getIsDelete,false);
List<DevicePo> devicePos = deviceMapper.selectList(queryWrapper);
if(CollectionUtils.isEmpty(devicePos)){
return Collections.emptyList();
}
Set<Integer> deviceIds = devicePos.stream().collect(Collectors.toMap(DevicePo::getDeviceId, DevicePo::getDeviceName)).keySet();
return deviceIds;
}
使用@Cacheable可以讀取數(shù)據(jù)并存儲(chǔ)到緩存中,這比較好理解。 在這里value則是代表了一個(gè)緩存池(請?jiān)试S我暫時(shí)先這么叫),表示從數(shù)據(jù)庫查詢出的數(shù)據(jù)會(huì)保存到名為devices的緩存池中。
在下次調(diào)用時(shí),則會(huì)先檢測緩存池devices中是否有數(shù)據(jù),如果有數(shù)據(jù),就直接返回,沒有數(shù)據(jù)就執(zhí)行方法,產(chǎn)生新的數(shù)據(jù)。
為什么返回的是id集合?而不是數(shù)據(jù)集合?
在還沒有講到新增和刪除緩存數(shù)據(jù)時(shí),可以理解為,是為了節(jié)省內(nèi)存的占用。
如果緩存的是數(shù)據(jù)集合,當(dāng)有一個(gè)【根據(jù)id獲取數(shù)據(jù)的方法時(shí)】就會(huì)造成內(nèi)存中緩存了兩份數(shù)據(jù),這無疑造成了緩存的浪費(fèi)。
本質(zhì)上,你可以理解,在緩存數(shù)據(jù)集合和單條數(shù)據(jù)時(shí),其實(shí)并沒有共用一塊數(shù)據(jù)內(nèi)存。只是非常關(guān)鍵的一個(gè)概念,千萬不要誤認(rèn)為數(shù)據(jù)集合和單條數(shù)據(jù)共用一塊數(shù)據(jù)內(nèi)存。
查詢單個(gè)數(shù)據(jù)
@Cacheable(value = "devices", key = "#id")
public DevicePo getById(Integer id) {
if (id == null) {
return null;
}
return deviceMapper.selectById(id);
}
相比于【獲取數(shù)據(jù)集合】,單條數(shù)據(jù)多了請求參數(shù),在@Cacheable注解中也多了key,雖然value都是devices,如果還是以【緩存池】來代表value的話,表面看【查詢所有數(shù)據(jù)集合方法】和【獲取單條數(shù)據(jù)】的@Cacheable注解value都為devices,好像共用了一個(gè)緩存池,而key則會(huì)被誤認(rèn)為是從【緩存池】中在數(shù)據(jù)集合中進(jìn)行了過濾,這也可能會(huì)讓人為認(rèn)為它們的數(shù)據(jù)地址也是相同的。
其實(shí),共用【緩存池】這么說也沒毛病,key作為過濾也沒毛病,但最后的結(jié)論是錯(cuò)的。
其實(shí)springBoot的cache緩存,你可以理解為是兩層嵌套的map,即Map<String,Map<String,Object>,map的key1為@Cacheable的value,key2為@Cacheable的key。
那【獲取數(shù)據(jù)集合方法】和【獲取單個(gè)數(shù)據(jù)方法】在這個(gè)map的存儲(chǔ)就變?yōu)榱耍?br />【獲取數(shù)據(jù)集合方法】: map.get("devices").put("all", list);
【獲取單個(gè)數(shù)據(jù)方法】: map.get("devices").put("1", obj); //假設(shè)數(shù)據(jù)id為1
到這里,在新增、修改、刪除時(shí),如果你還是一味的只是更新單條數(shù)據(jù)緩存,就會(huì)發(fā)現(xiàn)【獲取數(shù)據(jù)集合方法】為什么沒有更新了吧。
新增一條新的數(shù)據(jù)
@CachePut(value = "devices", key = "#result.id")這是一個(gè)很標(biāo)準(zhǔn)的新增數(shù)據(jù)的注解。但是這里面會(huì)有兩個(gè)問題。
首先,可能很多文章會(huì)這么寫:
@CachePut(value = "devices", key = "#device.id")
public Device add(Device device) {
...
}
這本身沒毛病,但是,你的請求參數(shù)中有id么? 我猜肯定沒有。那我換種寫法:
/**
* @return 返回設(shè)備id
*/
@CachePut(value = "devices", key = "#device.id")
public int add(Device device) {
...
}
如果返回的是數(shù)據(jù)id,或者boolean類型呢?寫入緩存的會(huì)是什么?還會(huì)是數(shù)據(jù)實(shí)體么?肯定不是,并且還會(huì)暴露上面那個(gè)問題,device中沒有id。
那為什么第一種寫法沒有問題? 你共用一個(gè)device實(shí)體,在填充id時(shí),請求參數(shù)中的id已經(jīng)被填充了。如果你的請求參數(shù)與返回的device不同,這個(gè)緩存也就不會(huì)保存,因?yàn)檎埱髤?shù)的id為null,key為null不會(huì)保存到緩存。
第二個(gè)問題,則是結(jié)合上面的查詢,如果你也有【獲取所有數(shù)據(jù)集合方法】,那這種寫法一定不會(huì)更新所有數(shù)據(jù)集合的緩存。
我們換種寫法:
@Caching(evict = {
@CacheEvict(value = "devices", allEntries = true),
@CacheEvict(value = "devices", key = "#result")
})
public int add(Device device) {
...
}
使用@Caching來處理,一個(gè)是刪除所有數(shù)據(jù)集合緩存,一個(gè)是刪除單條數(shù)據(jù)緩存,單條數(shù)據(jù)緩存的key則是用返回值(如果你的返回值是實(shí)體本身,也可以用put代替)
這樣在下次調(diào)用查詢接口時(shí),就會(huì)重新生成緩存。
更新一條數(shù)據(jù)
邏輯與新增相同,這里就不再贅述
刪除一條數(shù)據(jù)
邏輯與新增相同,這里就不再贅述
到此這篇關(guān)于SpringBoot中cache使用的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)SpringBoot cache使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?Boot中使用Spring?Retry重試框架的操作方法
這篇文章主要介紹了Spring?Retry?在SpringBoot?中的應(yīng)用,介紹了RetryTemplate配置的時(shí)候,需要設(shè)置的重試策略和退避策略,需要的朋友可以參考下2022-04-04
使用BitSet位集合,一個(gè)重復(fù)校驗(yàn)工具
這篇文章主要介紹了使用BitSet位集合,一個(gè)重復(fù)校驗(yàn)工具,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
詳解Java ScheduledThreadPoolExecutor的踩坑與解決方法
最近項(xiàng)目上反饋某個(gè)重要的定時(shí)任務(wù)突然不執(zhí)行了,很頭疼,開發(fā)環(huán)境和測試環(huán)境都沒有出現(xiàn)過這個(gè)問題。定時(shí)任務(wù)采用的是ScheduledThreadPoolExecutor,后來一看代碼發(fā)現(xiàn)踩了一個(gè)大坑。本文就來和大家聊聊這次的踩坑記錄與解決方法,需要的可以參考一下2022-10-10
Java多態(tài)概念、實(shí)現(xiàn)機(jī)制與實(shí)踐應(yīng)用詳解
多態(tài)是指同一個(gè)方法在不同對象上具有不同的行為,通過多態(tài)程序可以在運(yùn)行時(shí)決定調(diào)用哪個(gè)方法,從而提高代碼的靈活性和可擴(kuò)展性,這篇文章主要介紹了Java多態(tài)概念、實(shí)現(xiàn)機(jī)制與實(shí)踐應(yīng)用的相關(guān)資料,需要的朋友可以參考下2026-01-01
Java運(yùn)行時(shí)多態(tài)性的實(shí)現(xiàn)
Java運(yùn)行時(shí)多態(tài)性的實(shí)現(xiàn)...2006-12-12
vue3實(shí)現(xiàn)一個(gè)todo-list
這篇文章主要為大家詳細(xì)介紹了基于vuejs實(shí)現(xiàn)一個(gè)todolist項(xiàng)目,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能給你帶來幫助2021-08-08
Java Selenium實(shí)現(xiàn)多窗口切換的示例代碼
這篇文章主要介紹了Java Selenium實(shí)現(xiàn)多窗口切換的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

