项目场景:
实现ElementUI中三级分类的功能,发现没有前端三级目录的二级目录可以新建三级目录,数据库中也有数据,但是无法在前端显示!后端的接口没有返回数据库的数据。
问题描述
提示:这里描述项目中遇到的问题:
例如:数据传输过程中数据不时出现丢失的情况,偶尔会丢失一部分数据
APP 中接收数据代码:
@Override
public void run() {
bytes = mmInStream.read(buffer);
mHandler.obtainMessage(READ_DATA, bytes, -1, buffer).sendToTarget();
}
原因分析:
提示:例如:在
骑行运动
耳机目录之后新建的目录,前端界面显示新建成功,数据库也有数据,但是不会显示在前端。
数据库中新建数据成功,但是在骑行运动中不会显示出这个新建的婴儿车三级目录。
IDEA 日志打印记录显示插入成功
使用sql查询 parent_cid为128的二级目录
再使用接口测试前端界面没有婴儿车
代码:
后端接口
//controller层
/**
* 查出所有分类以及子分类,以树形结构组装起来
*/
@RequestMapping("/list/tree")
public R list(){
List<CategoryEntity> entities = categoryService.listWithTree();
return R.ok().put("data", entities);
}
//接口Service层
List<CategoryEntity> listWithTree();
Service的具体实现
@Override
public List<CategoryEntity> listWithTree() {
//1、查出所有分类
List<CategoryEntity> entities = baseMapper.selectList(null);
//2、 组装成父子的树形结构
//2.1) 找到所有一级分类(父分类id为0)
List<CategoryEntity> level1Menus = entities.stream().filter(categoryEntity ->
categoryEntity.getCatLevel() == 1
).map((menu)->{
// System.out.println(menu);
menu.setChildren(getChildrens(menu,entities));
// menu.setChildren(getSubtreeById(menu.getCatId(), entities));
return menu;
}).sorted((menu1,menu2)->{
return (menu1.getSort()==null?0:menu1.getSort()) - (menu2.getSort()==null?0:menu2.getSort());
}).collect(Collectors.toList());
return level1Menus;
}
//递归查找所有菜单的子菜单
private List<CategoryEntity> getChildrens(CategoryEntity root,List<CategoryEntity> all){
//1.找到当前菜单的子菜单(每个子菜单还有子菜单)
// Stream<CategoryEntity> categoryEntityStream = all.stream().filter(entity -> (long)entity.getParentCid() == (long)root.getCatId()); //出bug语句
Stream<CategoryEntity> categoryEntityStream = all.stream().filter(entity -> entity.getParentCid() == root.getCatId());
Stream<CategoryEntity> mapEntityStream = categoryEntityStream.map(item -> {
item.setChildren(getChildrens(item,all));
return item;
});
List<CategoryEntity> children = mapEntityStream.sorted((menu1,menu2)->{
return (menu1.getSort()==null?0:menu1.getSort()) - (menu2.getSort()==null?0:menu2.getSort());
}).collect(Collectors.toList());
return children;
解决方案:
提示:CatID为128以后的二级目录都不显示数据库中有的三级目录
1 数据库序号问题
DROP TABLE IF EXISTS `pms_category`;
CREATE TABLE `pms_category` (
`cat_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分类id',
`name` char(50) DEFAULT NULL COMMENT '分类名称',
`parent_cid` bigint(20) DEFAULT NULL COMMENT '父分类id',
`cat_level` int(11) DEFAULT NULL COMMENT '层级',
`show_status` tinyint(4) DEFAULT NULL COMMENT '是否显示[0-不显示,1显示]',
`sort` int(11) DEFAULT NULL COMMENT '排序',
`icon` char(255) DEFAULT NULL COMMENT '图标地址',
`product_unit` char(50) DEFAULT NULL COMMENT '计量单位',
`product_count` int(11) DEFAULT NULL COMMENT '商品数量',
PRIMARY KEY (`cat_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1424 DEFAULT CHARSET=utf8mb4 COMMENT='商品三级分类';
AUTO_INCREMENT=1424 原来的数据库是1434,改成了1424让cat_id自增 序号不断层,问题还是解决。 开始以为是数据库很多序号乱码,后面才发现是128二级目录开始出问题。
JAVA层面
1 递归改成非递归写法
//原版代码
// List<CategoryEntity> children = all.stream().filter(categoryEntity -> {
// return categoryEntity.getParentCid() == root.getCatId();
// }).map(categoryEntity->{
// //2.利用映射递归查找 子菜单的子菜单
// categoryEntity.setChildren(getChildrens(categoryEntity,all));
// return categoryEntity;
// }).sorted((menu1,menu2)->{
// //3.对当前菜单进行排序,升序排序
// return (menu1.getSort()==null?0:menu1.getSort()) - (menu2.getSort()==null?0:menu2.getSort());
// }).collect(Collectors.toList());
//
// return children;
//非递归写法
// List<CategoryEntity> result = new ArrayList<>();
// for (int i = 0; i < all.size(); i++){
// CategoryEntity entity = all.get(i);
// if (entity.getParentCid() == root.getCatId()) {
// result.add(entity);
// }
// }
// System.out.println(result);
//
// for (int i = 0; i < result.size(); i++){
// CategoryEntity current = result.get(i);
// List<CategoryEntity> sub = new ArrayList<>();
// for (int j = 0; j < all.size(); j++){
// CategoryEntity entity = all.get(j);
// if (entity.getParentCid() == current.getCatId()) {
// List<CategoryEntity> subsub = new ArrayList<>();
// for (int k = 0; k < all.size(); k++){
// CategoryEntity subEntity = all.get(k);
// if (subEntity.getParentCid() == entity.getCatId()){
// subsub.add(subEntity);
// }
// }
// entity.setChildren(subsub);
// sub.add(entity);
// }
// }
// current.setChildren(sub);
// }
//
// return result;
还是不行
重新写接口: 查询cat_id为128的子目录
//Controller层
/**
* 信息
*/
@RequestMapping("/info/{catId}")
//@RequiresPermissions("product:category:info")
public R info(@PathVariable("catId") Long catId) {
CategoryEntity category = categoryService.getById(catId);
return R.ok().put("data", category);
}
//Service层
List<CategoryEntity> getSubtreeById(long catId);
接口实现功能
@Override
public List<CategoryEntity> getSubtreeById(long catId) {
List<CategoryEntity> entities = baseMapper.selectList(null);
List<CategoryEntity> filterList = entities.stream().filter(item -> item.getParentCid() == catId)
.map(item -> {
item.setChildren(getSubtreeById(item.getCatId()));
return item;})
.collect(Collectors.toList());
return filterList;
}
前端端口测试: 直接通过id的方式能够查询到父ID为128的所有目录,既婴儿车的父目录找到了。
后续按照这个思路重新写程序能够实现三级分类展现所有子目录的功能,但是对bug产生的原因不太清晰!!!
解决方法
改变一行代码
就是因为递归调用了太多次,自动装箱成Long对象,但是Long对象装箱的源码实现跟128有很大的关系!! 下面解释中的Integer是一个道理,(Integer) 128 == (Integer) 128 为false,所以128之后的二级目录都不满足这个判断,所以他们的三级目录无法加载!!!
具体代码实现可以看引用!!!
Integer类 -128~127 之间的值都是直接从缓存中取出的,(Integer)127 == (Integer)127两边装箱后,实际指向堆内存中同一个对象,大于127 后就new一个新的对象返回。(Integer)128 == (Integer)128,装箱为引用类型后,没有做缓存,指向堆内存中不同对象,所以比较结果为false。至于为什么要缓存,若不缓存,每次都要new一个新对象,资源消耗多,所以缓存一些常用的数,来减少资源损耗。
————————————————
版权声明:本文为CSDN博主「有时候我也会」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43849277/article/details/108275997
总结
开发过程中不要随便用递归!!!Debug不好调整,遇到其他问题结合,真的是头皮发麻!!!本文只是精简的讲述了核心找bug的过程,但是Debug打各种条件断点,查看Tomcat源码,StackFlow看英文,,,等等困扰了我一天的bug,终于跟冠哥合理解决了!!!
推荐看一下阿里开发手册对递归使用的建议!!!