文章目录
概述
本文主要是为了个人练习spring的缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】,以及总结个人在使用过程中发现的疑惑点,以及如何解决等思路,另外该文章不会写的特别繁琐、复杂,如果具体想查看某个注解的详细使用方式请单个注解去CSDN查询即可,我这边只是为了个人总结,言简意赅那种。
疑惑点
思考:批量查询我放在缓存key=‘list’里面,而我在执行部分更新或者删除功能时,设置的key=’#id’,我以为部分更新和修改操作后,再次批量查询的结果就应该是更新缓存后的结果,实践才发现查询的还是旧数据,但我已经确认部分更新和删除操作缓存已经走进去了,为啥会这样?
答案:最终缓存中存储的内容格式为:Entry<key,value> 形式。
大白话讲:就是每个key,对应不同的表存储不同的信息,key=‘list’和key=’#id’,如果你不做关联它两一点关系都没有,所以如果你想实现更新操作能够修改并影响到批量查询的缓存结果,那么请把批量查询和修改功能对应相同的key
案例说明
EhCacheController
package com.example.demo.controller;
import com.example.demo.bean.UserModel;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* 练习SpringBoot本地缓存技术
* @Author 211145187
* @Date 2022/5/17 11:02
**/
@RestController
@RequestMapping("/ehCache")
public class EhCacheController {
private ConcurrentHashMap<String, List<UserModel>> cache = new ConcurrentHashMap<>();
@PostConstruct
public void initCacheUserModelList() {
List<UserModel> userModelList = new ArrayList<>();
userModelList.add(new UserModel(1, "Chris", 40, "中国上海"));
userModelList.add(new UserModel(2, "Marry", 20, "中国北京"));
userModelList.add(new UserModel(3, "Mike", 30, "中国广州"));
cache.put("userModelList", userModelList);
}
/**
* 缓存
* 固定key
*
* @return
*/
@GetMapping("/list")
@Cacheable(cacheNames = "user_model", key = "'list_all'")
public List<UserModel> list() {
List<UserModel> userModelList = cache.get("userModelList");
System.out.println("从数据库获取用户列表");
userModelList.stream().forEach(System.out::println);
return userModelList;
}
/**
* 缓存
* 固定key
*
* @param id
* @return
*/
@GetMapping("/find/{id}")
@Cacheable(cacheNames = "user_model", key = "'find_1'")
public UserModel find(@PathVariable int id) {
UserModel userModel = new UserModel(1, "Chris", 40, "中国上海");
System.out.println("从数据库获取用户");
return userModel;
}
/**
* 缓存
* 依照不同的请求参数进行缓存
* 相同参数读缓存,不同参数再缓存
*
* @param id
* @return
*/
@GetMapping("/findById/{id}")
@Cacheable(cacheNames = "user_model", key = "#id")
public UserModel findById(@PathVariable int id) {
UserModel userModel = new UserModel(id, "Chris", 40, "中国上海");
System.out.println("从数据库获取用户:" + id);
return userModel;
}
/**
* 修改缓存
* 调用这个方法,无论如何都会将结果写缓存
*
* @param userModel
* @return
*/
@PutMapping("/update")
@CachePut(cacheNames = "user_model", key = "#userModel.id")
public UserModel update(@RequestBody UserModel userModel) {
userModel = new UserModel(userModel.getId(), "Jerry", 28, "哈尔滨");
System.out.println("从缓存中修改用户,key = #userModel.id:" + userModel);
return userModel;
}
/**
* 修改缓存
* 调用这个方法,无论如何都会将结果写缓存
*
* @param userModel
* @return
*/
@PutMapping("/update2")
@CachePut(cacheNames = "user_model", key = "'list_all'")
public List<UserModel> update2(@RequestBody UserModel userModel) {
List<UserModel> userModelList = cache.get("userModelList");
Iterator<UserModel> iterator = userModelList.iterator();
while(iterator.hasNext()){
UserModel item = iterator.next();
if (item.getId() == userModel.getId()) {
iterator.remove(); //注意这个地方
}
}
userModelList.add(new UserModel(userModel.getId(), userModel.getName(), userModel.getAge(), userModel.getAddress()));
cache.put("userModelList", userModelList);
System.out.println("从缓存中修改用户,key = 'list_all'");
cache.get("userModelList").stream().forEach(System.out::println);
return userModelList;
}
/**
* 删除缓存
* 调用删除key匹配的缓存
*
* @param id
* @return
*/
@DeleteMapping("/delete/{id}")
@CacheEvict(cacheNames = "user_model", key = "#id")
public String delete(@PathVariable int id) {
System.out.println("从缓存删除用户:" + id);
return "Delete User " + id + " success.";
}
}
1.验证:【批量查询+修改】 指向key相同结果是否生效?
答案:生效
第1步:调用批量查询,@Cacheable(cacheNames = “user_model”, key = “‘list_all’”)
第2步:调用更新,@CachePut(cacheNames = “user_model”, key = “‘list_all’”)
第3步:再次调批量查询,@Cacheable(cacheNames = “user_model”, key = “‘list_all’”)
第4步:看控制台打印
总结:已经走了本地缓存,因为咱们前后调用2次批量查询,但控制台只打印一次。
2.验证:【单条查询+单条修改/单条删除】 指向key相同结果是否生效?
答案:生效
第1步:调用单条查询,@Cacheable(cacheNames = “user_model”, key = “#id”)
第2步:调用删除,@CacheEvict(cacheNames = “user_model”, key = “#id”)
第3步:再次调用单条查询,@Cacheable(cacheNames = “user_model”, key = “#id”)
第4步:看控制台打印
总结:
正常第2次单条查询会走缓存,且控制台不会打印,但由于单条查询后 =》 删除 =》 再单条查询,所以单条查询打印了2次,等第3次再次调用单条查询后续就不会打印了
3.验证:【批量查询+修改】指向key不相同结果是否生效?
答案:不生效
第1步:调用批量查询,@Cacheable(cacheNames = “user_model”, key = “‘list_all’”)
第2步:调用更新,@CachePut(cacheNames = “user_model”, key = “#userModel.id”)
第3步:再次调用批量查询,@Cacheable(cacheNames = “user_model”, key = “‘list_all’”)
第4步:看控制台打印
总结:
结论:执行修改后查询的还是旧数据
原因:最终缓存中存储的内容格式为:Entry<key,value> 形式。因为指向的key不相同,实际相当于保存了不同的数据,所以如果想影响相同的数据,请指向相同的key才行。
本人其他相关文章链接
1.Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项