三级分类的数据表设计和构造API数据

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 三级分类的数据表设计和构造API数据

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情

如此的业务需求应该说是每个项目中的基本吧。

诸如下图这种:

1704465543331.jpg

一级菜单下有二级菜单和三级菜单。与此类似的应用场景还有很多很多,如省市县的三级联动、后台管理菜单、部门展示等等。

一、数据表设计

先看看表结构吧

1704465550719.jpg

具体的 sql 文件,在贴的源码仓库中有。

其中最重要的就是理清父子层级的关系就好了,知道是个什么样子设计的,如何去管理他们,那这些就都不是事啦。

二级评论的表的设计方法其实也大致如此。

数据表中对应的数据关系大致如下:

1704465552948.jpg

二、项目代码

技术就是常用的 SpringBoot、Mybatis-Plus

另外还用了下Mybatis-Plus逆向生成器,还有Java的函数式编程

3.1、导入依赖

完整依赖如下:

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.5.2</version>
    </parent>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.6</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--进行Spring Boot配置文件部署时,发出警告Spring Boot Configuration Annotation Processor not configured,但是不影响运行
            它的意思是“Spring Boot配置注解执行器没有配置”,配置注解执行器的好处是什么。
            配置注解执行器配置完成后,当执行类中已经定义了对象和该对象的字段后,在配置文件中对该类赋值时,便会非常方便的弹出提示信息。
        -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <!--mybatis-plus逆向工程-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

关于 Mybatis-plus 的版本,最新的那个逆向生成器不太熟,就用了旧版本的。感兴趣的可以去试一试新版本的。

3.2、逆向生成代码

import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
import org.junit.platform.commons.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
// 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
public class CodeGenerator {
    /**
     * <p>
     * 读取控制台内容
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotBlank(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }
    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        final String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("nzc");
        gc.setOpen(false);
        // gc.setSwagger2(true); 实体属性 Swagger2 注解
        mpg.setGlobalConfig(gc);
        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/task1?useUnicode=true&useSSL=false&characterEncoding=utf8");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        mpg.setDataSource(dsc);
        // 包配置
        final PackageConfig pc = new PackageConfig();
        pc.setModuleName(scanner("模块名"));
        pc.setParent("com.nzc");
        mpg.setPackageInfo(pc);
        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };
        // 如果模板引擎是 freemarker
        //String templatePath = "/templates/mapper.xml.ftl";
        // 如果模板引擎是 velocity
        String templatePath = "/templates/mapper.xml.vm";
        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
                return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);
        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();
        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);
        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        // 公共父类
        // 写于父类中的公共字段
        strategy.setSuperEntityColumns("id");
        strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix("pms_");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new VelocityTemplateEngine());
        mpg.execute();
    }
}

运行代码:

1704465575590.jpg

执行结果

1704465578400.jpg

默认里面是没有方法的话,不过用来写个基本的小demo是非常方便的。

3.3、业务代码

controller、mapper、entity都不是重点,一笔带过了哈。

@RestController
@RequestMapping("/category")
public class CategoryController {
    @Autowired
    ICategoryService categoryService;
    /**
     * 查询出所有的数据,以树形结构组装起来
     * @return
     */
    @GetMapping("/tree")
    public Result listTree() {
        List<Category> entities= categoryService.listWithTree();
        return ResultUtil.success(entities);
    }
}
public interface CategoryMapper extends BaseMapper<Category> {
}
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("pms_category")
public class Category implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId(value = "cat_id", type = IdType.AUTO)
    private Long catId;
    private String name;
    private Long parentCid;
    private Integer catLevel;
    private Integer showStatus;
    private Integer sort;
    private String icon;
    private String productUnit;
    private Integer productCount;
    @TableField(exist = false)
    private List<Category> categoryChild;
}
public interface ICategoryService extends IService<Category> {
    /**
     * 封装成三级菜单形式
     * @return
     */
    List<Category> listWithTree();
}

真正的业务是在 serviceImpl 中

@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements ICategoryService {
//    @Autowired 
//    CategoryMapper categoryMapper;
    @Override
    public List<Category> listWithTree() {
        //1、查询出所有的分类
        List<Category> allCategory = baseMapper.selectList(null);
        //2、组装成父子的树形结构
        //2.1、找到所有的一级分类
        List<Category> categoriesLevel1 = allCategory.stream()
              // filter 方法就如名称一样,就是用来过滤的 
              // 满足条件的会留下,到最后会返回一个流式对象
                .filter(category ->
                        category.getParentCid() == 0
                )
              // map 的理解百话点说就是可以对传入的对象做出修改 
              // 这里的意思就是找出当前一级分类中的二级分类,然后再set进去
                // 最后再返回一个流式对象
                .map((menu) -> {
                    menu.setCategoryChild(getChildrens(menu, allCategory));
                    return menu;
                })
              // 见名知意,这就是排序 ,其实也算是重定义Java比较器
               .sorted((menu1, menu2) -> {
                    return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
                })
              // 就是将流转换为带泛型的List对象
                .collect(Collectors.toList());
        return categoriesLevel1;
    }
    private List<Category> getChildrens(Category root, List<Category> all) {
        // 这里的逻辑和上面一段是一样的,这里是构造三级、四级等等多层也是一样构造的
        List<Category> collect = all.stream()
                .filter(category -> {
                    return category.getParentCid().equals(root.getCatId());
                }).map((menu) -> {
                    menu.setCategoryChild(getChildrens(menu, all));
                    return menu;
                })
                .sorted((menu1, menu2) -> {
                    return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
                })
                .collect(Collectors.toList());
        return collect;
    }
}

还有一些公共代码和配置文件什么的,在仓库中都是有的,这里不再重复贴出来啦。

3.4、测试结果

我就是使用 Postman 测试的,没有专门画前端,前端太弱鸡啦,求大佬们放过...

1704465601216.jpg

源码:githubgitee

三、自言自语

真正到了工作的时候,你会发现你写代码和平常还是会有很大差别的,最主要就是体现在业务场景这个方面。

写 Demo 的时候,主要就想着能写出个 Demo 就不错了,但实际上真到了该上业务的时候,综合起来还是有很多值得思考的问题。

在学习的时候针对一些必要的技术还是可以往深里挖掘挖掘的,并且自己也要学会根据现学的东西想到应用场景,思考如何整合项目,那些地方还有不足的,项目的扩展性如何等等,这些可以不用实现,但是在学习的时候能够多一些思考,这对于学习一个新技术是很有必要的。

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
1月前
|
存储 数据挖掘 BI
API数据源:轻松接入各类业务系统数据
在数字化转型中,企业面临多样化的数据需求。Quick BI推出API数据源功能,支持广泛的数据接入,包括实时天气、电商交易及内部业务数据,极大丰富了可分析数据范围。该功能提供灵活的连接方式(抽取和直连模式)、多元授权机制(基础认证、前置请求)和自动化数据解析,降低了操作门槛,提升了配置效率。通过动态Token获取等最佳实践,确保数据安全与实时性,满足企业具体业务需求。了解更多,请访问Quick BI官方文档或瓴羊官网。
162 77
|
3天前
|
JSON Shell API
如何通过API获取淘宝商品月销售数据
淘宝开放平台提供了丰富的API接口,允许开发者获取商品的详细信息,包括月销售数据。
|
3天前
|
数据采集 JSON Java
Java爬虫获取微店快递费用item_fee API接口数据实现
本文介绍如何使用Java开发爬虫程序,通过微店API接口获取商品快递费用(item_fee)数据。主要内容包括:微店API接口的使用方法、Java爬虫技术背景、需求分析和技术选型。具体实现步骤为:发送HTTP请求获取数据、解析JSON格式的响应并提取快递费用信息,最后将结果存储到本地文件中。文中还提供了完整的代码示例,并提醒开发者注意授权令牌、接口频率限制及数据合法性等问题。
|
6天前
|
XML 数据挖掘 API
1688商品详情数据示例参考,1688API接口系列
在成长的路上,我们都是同行者。这篇关于详情API接口的文章,希望能帮助到您。期待与您继续分享更多API接口的知识,请记得关注Anzexi58哦!
|
13天前
|
数据采集 监控 搜索推荐
深度解析淘宝商品详情API接口:解锁电商数据新维度,驱动业务增长
淘宝商品详情API接口,是淘宝开放平台为第三方开发者提供的一套用于获取淘宝、天猫等电商平台商品详细信息的应用程序接口。该接口涵盖了商品的基本信息(如标题、价格、图片)、属性参数、库存状况、销量评价、物流信息等,是电商企业实现商品管理、市场分析、营销策略制定等功能的得力助手。
|
8天前
|
人工智能 数据挖掘 API
淘宝评论与商品详情数据API接口的使用与应用
商品详情数据API:获取商品的基本信息(如标题、价格、库存、描述等)。 评论数据API:获取商品的用户评价数据(如评分、评论内容、用户昵称、购买时间等)。
|
24天前
|
搜索推荐 数据挖掘 API
怎么利用商品详情 API 接口实现数据获取与应用?
在电商蓬勃发展的时代,数据成为驱动业务增长的关键。商品详情API接口为电商从业者、开发者和数据分析爱好者提供了获取海量商品数据的途径,助力精准营销、优化用户体验和提升运营效率。本文深入探讨如何利用商品详情API接口进行数据获取与应用,涵盖接口概念、工作原理、不同平台特点、准备工作、数据获取及处理、错误处理,并通过代码示例展示其在电商平台展示、数据分析、竞品分析和个性化推荐等场景中的应用。
41 12
|
30天前
|
JSON 缓存 API
解析电商商品详情API接口系列,json数据示例参考
电商商品详情API接口是电商平台的重要组成部分,提供了商品的详细信息,支持用户进行商品浏览和购买决策。通过合理的API设计和优化,可以提升系统性能和用户体验。希望本文的解析和示例能够为开发者提供参考,帮助构建高效、可靠的电商系统。
39 12
|
23天前
|
搜索推荐 API 开发者
深度解析:利用商品详情 API 接口实现数据获取与应用
在电商蓬勃发展的今天,数据成为驱动业务增长的核心。商品详情API接口作为连接海量商品数据的桥梁,帮助运营者、商家和开发者获取精准的商品信息(如价格、描述、图片、评价等),优化策略、提升用户体验。通过理解API概念、工作原理及不同平台特点,掌握获取权限、构建请求、处理响应和错误的方法,可以将数据应用于商品展示、数据分析、竞品分析和个性化推荐等场景,助力电商创新与发展。未来,随着技术进步,API接口将与人工智能、大数据深度融合,带来更多变革。
62 3
|
26天前
|
缓存 监控 API
如何查看商品销量 API 接口的性能指标数据
在电商蓬勃发展的时代,数据驱动业务决策至关重要。商品销量作为核心指标,依赖高效稳定的API接口获取。本文探讨如何查看和优化商品销量API的性能指标,包括响应时间、吞吐量、错误率和并发用户数,通过专业工具、日志分析及自定义代码实现监控与优化,确保业务稳定运行和用户体验提升。
52 2

热门文章

最新文章