类目简介
上述图片的箭头指向的便是类目
功能描述
- 添加类目:完成单个类目信息的录入
- 查询单个类目:根据类目主键查询类目详情信息以及它所对应的子类目信息
- 查询所有类目信息:查询出所有类目信息以及它的所有子类目信息
领域模型
将信息抽象为对应的领域模型
- 定义子类目数据使用List类型是因为可能存在多个子类目
- 为了方便查询我们在模型上加了父类目id,可以根据一个类目id即可查询其所有的子类目信息
类目基础操作
由于大型网站平台每天可能有上亿人访问,如果没访问一次类目就需要调用一次数据库,那这样对数据库的压力太大了
由于访问频次较高,所以对他进行优化需要使用Redis列表结构
列表头元素和尾元素:
- 头元素指列表左端第一个元素
- 尾元素指列表右端第一个元素
列表操作
1.插入数据
Redis列表结构就是List,现在将下面的信息录入到Redis中去
@Controller
public class CategoryApi {
@Autowired
private RedisTemplate redisTemplate;
@PostMapping("/addList")
@ResponseBody
public Result add() {
Result result = new Result();
result.setSuccess(true);
Category firstCategory = new Category();
firstCategory.setId("gcl_001");
firstCategory.setName("女装");
firstCategory.setDescription("女装");
Category secondCategory = new Category();
secondCategory.setId("gcl_sub_001");
secondCategory.setName("连衣裙");
secondCategory.setParentCategoryId("gcl_001");
secondCategory.setDescription("连衣裙");
Category thirdCategory = new Category();
thirdCategory.setId("gcl_sub_002");
thirdCategory.setName("半身裙");
thirdCategory.setParentCategoryId("gcl_001");
thirdCategory.setDescription("半身裙");
Category fouthCategory = new Category();
fouthCategory.setId("gcl_sub_003");
fouthCategory.setName("毛针织衫");
fouthCategory.setParentCategoryId("gcl_001");
fouthCategory.setDescription("毛针织衫");
Category fifthCategory = new Category();
fifthCategory.setId("gcl_sub_004");
fifthCategory.setName("卫衣");
fifthCategory.setParentCategoryId("gcl_001");
fifthCategory.setDescription("卫衣");
//leftPush也就是从左到右依次插入数据
redisTemplate.opsForList().leftPush("categoryList", firstCategory);
redisTemplate.opsForList().leftPush("categoryList", secondCategory);
redisTemplate.opsForList().leftPush("categoryList", thirdCategory);
redisTemplate.opsForList().leftPush("categoryList", fouthCategory);
redisTemplate.opsForList().leftPush("categoryList", fifthCategory);
return result;
}
}
使用Redis存储读取的对象必须都能够序列化,对象需要经过网络传输才能存到远程的Redis服务器中,所以Category需要实现
java.io.Serializable
接口,序列化的过程由RedisTemplate
自动实现
2.查询数据
@PostMapping("/queryAll")
@ResponseBody
public Result<List<Category>> queryAll(){
Result result = new Result();
result.setSuccess(true);
//查询所有的类目数据
List<Category> categoryDatas = redisTemplate.opsForList().range("categoryList",0,-1);
//处理父子类目关系
List<Category> allFirstCats = handleParentData(categoryDatas);
result.setData(allFirstCats);
return result;
}
/**
* 处理父子类目关系数据
*
* @param categoryDatas 一组类目数据
*/
private List<Category> handleParentData(List<Category> categoryDatas){
if (CollectionUtils.isEmpty(categoryDatas)){
//防止空指针异常,返回一个没有元素的List
return new ArrayList<>();
}
Map<String,Category> categoryMap = new HashMap<>();
//初始化一个虚拟根节点,0 可以对应的是所有一级类目的父亲
categoryMap.put("0",new Category());
//把所有的类目转换为 map 数据
categoryDatas.forEach(category -> categoryMap.put(category.getParentCategoryId(),category));
//再次遍历类目数据,处理父子关系
categoryDatas.forEach(category -> {
// 得到父实例
String pId = category.getParentCategoryId();
// 没有父类目 Id,当做 0 处理,作为虚拟根节点的子类目,就是所有的一级类目
if (StringUtils.isBlank(pId)) {
pId = "0";
}
Category parent = categoryMap.get(pId);
if (parent!=null){
//初始化子类目
if (parent.getSubCategoryList()==null){
parent.setSubCategoryList(new ArrayList<>());
}
//在父类目中添加子类目
parent.getSubCategoryList().add(category);
}
});
//得到所有的一级类目
List<Category> data = categoryMap.get("0").getSubCategoryList();
return data;
}
range("categoryList",0,-1)
方法的作用就是查询categoryList列表的的值
- 第一个值是key
- 第二个值是起始位置
- 第三个值是结束位置.-1表示取得全部
3.删除数据
@GetMapping("/delListItem")
@ResponseBody
public Result<List<Category>> delListItem() {
Result<List<Category>> result = new Result<>();
result.setSuccess(true);
redisTemplate.opsForList().leftPop("categoryList");
redisTemplate.opsForList().rightPop("categoryList");
List<Category> subCategoryLists = redisTemplate.opsForList().range("categoryList", 0, -1);
result.setData(subCategoryLists);
return result;
}
- leftPop()删除头元素
- rightPop()删除尾元素