主要用于管理后台添加商品分类使用。
controller-backend-****Controller
后台分类功能模块:
获取节点
增加节点
修改名字
获取分类ID
递归子节点ID
...
学习目标:
◆如何设计及封装无限层级的树状数据结构-数据设计
◆递归算法的设计思想
◆如何处理复杂对象排重
◆重写hashcode和equal以及重写注意事项
数据表设计:
/**
* 管理后台-商品分类管理
* 1.新增分类
* 2.更新分类名称
* 3.查询分类(同级)
* 4.查询分类及其分类下的其他子分类(遍历所有子节点-子节点-...)
* 以上操作都必须在登录状态下进行
*/
1.增加分类节点
需要从session中判断用户是否登录(因为是后台,这里用户都是管理者),若未登录则返回强制登录。
前台传递传输:
categoryName-分类名
parentId-所属节点层级,若前台未传,则默认是根节点-0(Controller在接收参数时可通过@RequestParam(value="parentId",defaultValue=0))
逻辑点:
为了数据的安全,需要去校验当前操作的用户是否是管理员,防止用户权限横向越权。
2.更新分类节点名称
3.获取当前分类节点下的信息(平级子节点不递归)
请求参数:CategoryId-请求参数不传则通过@RequestParam将值置位默认的0,即查询跟节点下的平级子节点(查询当前节点下所有的子节点,其实就是查询其他数据的parent_id,这个字段指定了他的上级节点)
如果返回的查询结果集为空,我们是不需要返回给前端,可以让查询为空的结果的消息记录打印到日志里面里面去
private Logger logger = LoggerFactory.getLogger(CategoryServiceImpl.class)
日志使用:logger.info("未找到到当前分类的子分类")
4.获取当前分类节点id,并且递归查询子节点id(递归查询所有的子孙节点)
如当前分类节点是0,该节点下有两个子节点0001和0002,其中0001节点自己本身还有0001001和0001010两个子节点。那么逻辑是如果查询节点0001就要返回该0001001和00011010两个子节点;如果查询节点0,就要返回0001节点和0002节点(子节点)以及0001节点下的0001001、0001010节点(孙子节点)
》拿一个categoryId去查询他的子节点,同时判断子节点下是否还有子节点。
逻辑:
a-获取前端传递而来的分类id-categoryId
b-取数据表匹对当前id是否有数据。如果有数据将当前id查询到的数据记录到Set集合中,同时将该id作为查询子节点parentId的参数;
c-上一步拿到的id去数据表中比对字段parentId是否有数据(parentId是数据表中用于跟踪所属上级分类的字段,通过它就可以一层层往上找)
d-如果有数据,就需要循环拿出查询到的数据,并调用自己,一方面是将查询到的数据记录Set集合中,另一方那个面是新拿到的id去看看这个分类id它是不是也有下属数据。
e-依次循环,直到数据查询完毕
通过set类对int类型的数据去重,要重写hashCode和equals()方法,(Set对String类型的去重较好,因为它本身进行了hashCode和equal()重写)
在实体了Pojo中对要排重的变量,重写hashCode()和equals()方法(Generate equal and hashCode())
Tips:在项目中如要用Set集合去比对重复,最好要hashCode和equal()一起重写,保证里面的判断银子是一样的。
实现:返回的是分类id
// 查询本节点的id及孩子节点的id
public ServerResponse<List<Integer>> selectCategoryAndChildrenById(Integer categoryId){
Set<Category> categorySet = Sets.newHashSet(); //初始化Set集合(guava包)
findChildCategory(categorySet,categoryId);// 递归查询节点下的节点-categorySet用于存放结果
List<Integer> categoryIdList = Lists.newArrayList(); //初始化结果序列,该序列用于保存最后的id
if(categoryId != null){
for(Category categoryItem : categorySet){
//foreach-循环取出categorySet中存放了值,这个值可能是多层的所以要递归取出
categoryIdList.add(categoryItem.getId()); // 添加到list结果序列中,用于返回
}
}
return ServerResponse.createBySuccess(categoryIdList);
}
//递归算法,算出子节点--Set<Category> categorySet 这个是参数也是一个返回集合 ,Integer categoryId-参数
private Set<Category> findChildCategory(Set<Category> categorySet ,Integer categoryId){
Category category = categoryMapper.selectByPrimaryKey(categoryId);
//数据库查询所有的categoryId的数据,注意这里拿的所有的id-即父亲节点
if(category != null){
categorySet.add(category); //结果放入Set集合中,用于下一步的递归取出
}
//这里的categoryId是传入数据库和表中的联系所属父亲节点的parentId查询,取当前父节点所有的子节点,如此循环就会得到一些重复的父节点,因此需要Set去重
List<Category> categoryList = categoryMapper.selectCategoryChildrenByParentId(categoryId);
//查找子节点,递归算法一定要有一个退出的条件,Mybatis不会返回空集,如果用其他的框架要注意空集判断
for(Category categoryItem : categoryList){
//取出selectCategoryChildrenByParentId查询到的子节点的id,再递归调用自己,一方面将结果记录set中,另一方面再往下查询该id下的子节点
findChildCategory(categorySet,categoryItem.getId());
}
return categorySet;
}
接口:
@RequestMapping(value = "get_deep_category.do",method = RequestMethod.GET)
@ResponseBody
public ServerResponse getCategoryParentAndChildren(String categoryId,HttpSession session){
if ( categoryId ==null){
return ServerResponse.createByErrorMsg("请求参数不能为空");
}
User user = (User)session.getAttribute(Const.CURRENT_USER);
if (user==null){
return ServerResponse.createByErrorCodeMsg(ResponseCode.NEED_LOGIN.getCode(),"请登录后在操作");
}
if (user.getRole().equals(Const.CUSTOMER)){
return ServerResponse.createByErrorMsg("当前用户无操作权限");
}
return iCategoryService.getCategoryParentAndChildren(Integer.parseInt(categoryId));
}