实现父子层级的数据结构封装

前言

在今天的项目中,需要将商品分类信息封装成父子层级的数据结构

一、相关数据准备

1.分类父子层级示例展示

[
  {
    "index": 1,
    "categoryChild": [
      {
        "categoryChild": [
          {
            "categoryName": "电子书", 
            "categoryId": 1
          },
          {
            "categoryName": "网络原创", 
            "categoryId": 2
          }
        ],
        "categoryName": "电子书刊",
        "categoryId": 1
      }
    
    ],
    "categoryName": "图书、音像、电子书刊",
    "categoryId": 1
  },
  
      {
"index": 2,
    "categoryChild": [
      {
        "categoryChild": [
          {
            "categoryName": "超薄电视", 
            "categoryId": 1
          },
          {
            "categoryName": "全面屏电视", 
            "categoryId": 2
          },
          
        ],
        "categoryName": "电视",
        "categoryId": 1
      },
    
    ],
    "categoryName": "家用电器",
    "categoryId": 2
  }
]

2.数据库相关展示

2.1涉及的表

在这里插入图片描述
这其中,base_category1中的id与base_category2的category1_id相关联,base_category2中的id与base_category3中的category2_id相关联,他们之间有一对多、多对一的关系

2.2 建立视图

我们可以建立一个视图(view),把三张表关联起来,视图id就是三级分类id,这样通过三级分类id就可以查询到相应数据,效果如下:
在这里插入图片描述
建立视图相关代码:

create view base_category_view2 as
select c3.id id,
       c1.id category1_id, c1.name category1_name,
       c2.id category2_id,c2.name category2_name,
       c3.id category3_id,c3.name category3_name
from base_category1 c1
inner join base_category2 c2
on c1.id=c2.category1_id
inner join base_category3 c3
on c2.id=c3.category2_id

二、具体实现

//根据分类父子层级示例封装后台数据
public List<JSONObject> getBaseCategoryList() {
        List<JSONObject> jsonObjectList = new ArrayList<>();
        //获取首页分类集合 这一步,是利用mybatisplus先从刚刚创建的视图中
        //查询到所有数据
        List<BaseCategoryView> baseCategoryViews = baseCategoryViewMapper.selectList(null);

        /**
         * 首先按照一级分类进行分组
         * 使用stream流的方式将集合根据category1_id分组成一个以category1_id为键,
         * List<BaseCategoryView>为值的map集合
         */
        Map<Long, List<BaseCategoryView>> category1Map = baseCategoryViews.stream().collect(Collectors.groupingBy(new Function<BaseCategoryView, Long>() {
            @Override
            public Long apply(BaseCategoryView baseCategoryView) {
                return baseCategoryView.getCategory1Id();
            }
        }));

        //设置一级分类的index 遍历一级分类分组以后的map集合
        int index = 1;
        for (Map.Entry<Long, List<BaseCategoryView>> category1KeyAndValue : category1Map.entrySet()) {
            //这个jsonObject1对象必须每次循环都要new,如果放在循环外层,jsonObjectList
            //中的所有值被最后一次jsonObject1所put的键值对所覆盖,jsonObject1 和 jsonObject1同理
            JSONObject jsonObject1 = new JSONObject();
            //设置一级分类信息
            jsonObject1.put("index", index++);
            jsonObject1.put("categoryName", category1KeyAndValue.getValue().get(0).getCategory1Name());
            jsonObject1.put("categoryId", category1KeyAndValue.getValue().get(0).getCategory1Id());
            /**
             * 需要的二级分类子节点必须要等遍历完二级分类信息,因此先进行遍历二级分类信息
             * 而二级分类想要设置三级分类子节点,就必须先遍历三级分类信息,又因为一级分类对应
             * 多个二级分类,二级分类又对应多个三级分类,因此需要层层嵌套从里到外,有了三级分类
             *遍历后的全部信息才能设置二级分类的三级分类子节点,之后再遍历完二级分类的全部信息才
             * 能设置一级分类的二级分类子节点
             */
            //遍历二级分类信息
            List<JSONObject> jsonObjectList2 = new ArrayList<>();
            Map<Long, List<BaseCategoryView>> category2Map = category1KeyAndValue.getValue().stream().collect(Collectors.groupingBy(BaseCategoryView::getCategory2Id));
            for (Map.Entry<Long, List<BaseCategoryView>> category2KeyAndValue : category2Map.entrySet()) {
                JSONObject jsonObject2 = new JSONObject();
                jsonObject2.put("categoryName", category2KeyAndValue.getValue().get(0).getCategory2Name());
                jsonObject2.put("categoryId", category2KeyAndValue.getValue().get(0).getCategory2Id());
                //遍历三级分类信息
                List<JSONObject> jsonObjectList3 = new ArrayList<>();
                Map<Long, List<BaseCategoryView>> category3Map = category2KeyAndValue.getValue().stream().collect(Collectors.groupingBy(BaseCategoryView::getCategory3Id));
                for (Map.Entry<Long, List<BaseCategoryView>> category3KeyAndValue : category3Map.entrySet()) {
                    JSONObject jsonObject3 = new JSONObject();
                    jsonObject3.put("categoryName", category3KeyAndValue.getValue().get(0).getCategory3Name());
                    jsonObject3.put("categoryId", category3KeyAndValue.getValue().get(0).getCategory3Id());
                    //将每组三级分类信息加入三级分类集合
                    jsonObjectList3.add(jsonObject3);
                }
                //设置二级分类的子节点信息(三级分类信息)
                jsonObject2.put("categoryChild", jsonObjectList3);
                //将每组二级分类信息加入二级分类集合
                jsonObjectList2.add(jsonObject2);
            }
            //设置一级分类的子节点信息(二级分类信息)
            jsonObject1.put("categoryChild", jsonObjectList2);

            //收集全部一级分类信息
            jsonObjectList.add(jsonObject1);
        }
        //返回包含完整的一、二、三级分类信息
        return jsonObjectList;


    }

三、 总结

1.java中的stream流

Stream流是一种函数式编程的概念,它可以用来处理集合或数组等数据源的元素.

1.1优点

1.Stream流提供了一种简洁的方式来处理集合中的元素,可以通过链式操作来实现复杂的数据处理逻辑,减少了代码的编写量
2.Stream流支持并行处理,可以自动将数据源分成多个部分并行处理,提高了处理大数据量的效率。
3.Stream流中的操作是延迟计算的,只有在终止操作时才会执行,这样可以根据需要灵活地选择执行哪些操作,提高了程序的性能。
4.Stream流鼓励使用函数式编程的思想,可以通过Lambda表达式来定义操作,使代码更加简洁、易读。

1.2缺点

1.可读性:虽然Stream流可以使代码更加简洁,但有时候也可能会降低代码的可读性,特别是对于复杂的操作逻辑,可能需要花费更多的时间来理解代码。
2.不可变性:Stream流中的操作是基于不可变对象的,每次操作都会返回一个新的Stream流,这可能会导致频繁的对象创建和销毁,对于大数据量的处理可能会带来一定的性能开销。
3.性能问题:虽然Stream流支持并行处理,但并不是所有的场景都适合并行处理,有时候并行处理反而会降低性能,需要根据具体情况进行评估和优化。

2.mysql中的视图

MySQL中的视图(View)是一种虚拟表,它是基于一个或多个表的查询结果集,并且可以像表一样被查询和使用。

2.1 优点

1.它可以简化复杂查询,将复杂的查询逻辑封装在一个视图中,使得查询变得简单明了,减少了重复编写复杂查询的工作量。
2.通过视图,还可以限制用户只能访问他们需要的数据,可以对视图进行权限控制,提高了数据的安全性。
3.视图可以隐藏底层表的结构和细节,将数据的逻辑关系抽象出来,提供更高层次的数据抽象,简化了数据操作。
4.通过视图,可以将对底层表的复杂数据修改操作简化为对视图的修改操作,减少了数据操作的复杂性。

2.2缺点

1.性能问题:视图在查询时需要实时计算,可能会引入一定的性能开销,特别是当视图基于复杂查询或多个表时,可能会导致查询变慢。
2.存储空间占用:视图本身并不存储数据,但是会占用一定的存储空间,特别是当视图基于大量数据或复杂查询时,可能会占用较大的存储空间。
3.更新限制:视图一般是只读的,对于基于多个表的视图,可能无法直接进行更新操作,需要对底层表进行更新。
4.版本控制问题:当底层表结构发生变化时,可能会导致视图无法正常使用,需要及时更新视图以适应底层表的变化。

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值