MySQL树形结构表设计

两个字段:

  • pid:父级ID
  • parent_ids:所有经过的路径节点ID

这样设计有个好处是,可以查任意节点的所有子节点,从任意节点开始既可以向上查,也可以向下查

select * from enterprise where find_in_set(4, parent_ids); 

构造菜单树(Java实现)

@Data
@NoArgsConstructor
public class Menu {
    private Integer id;
    private Integer pid;    //  父菜单ID
    private String pids;    //  所有父菜单ID集合(按顺序,逗号分隔)
    private String name;
    private String code;
    private int sort;
    List<Menu> children;

    public Menu(Integer id, Integer pid, String name, int sort) {
        this.id = id;
        this.pid = pid;
        this.name = name;
        this.sort = sort;
    }
}

第一种写法

@Test
void contextLoads() throws Exception {
    List<Menu> allMenuList = new ArrayList<>();
    allMenuList.add(new Menu(1, 0, "一级菜单A", 1));
    allMenuList.add(new Menu(2, 0, "一级菜单B", 2));
    allMenuList.add(new Menu(3, 1, "二级菜单AA", 1));
    allMenuList.add(new Menu(4, 2, "二级菜单BB", 1));
    allMenuList.add(new Menu(5, 3, "三级菜单AAA", 2));
    allMenuList.add(new Menu(6, 4, "三级菜单BBB", 2));
    allMenuList.add(new Menu(7, 1, "二级菜单AC", 2));
    
    //  找到所有一级菜单
    List<Menu> parentList = allMenuList.stream().filter(e->e.getPid().equals(0)).collect(Collectors.toList());
    for (Menu menu : parentList) {
        menu.setChildren(getChild(menu.getId(), allMenuList));
    }
}

/**
 * 递归查找子菜单
 */
public List<Menu> getChild(Integer pid, List<Menu> allMenuList) {
    List<Menu> childList = new ArrayList<>();
    for (Menu menu : allMenuList) {
        if (pid.equals(menu.getPid())) {
            menu.setChildren(getChild(menu.getId(), allMenuList));
            childList.add(menu);
        }
    }
    childList.sort(Comparator.comparing(Menu::getSort).reversed());
    return childList;
}

第二种写法

@Test
void contextLoads() throws Exception {
    List<Menu> allMenuList = new ArrayList<>();
    allMenuList.add(new Menu(1, 0, "一级菜单A", 1));
    allMenuList.add(new Menu(2, 0, "一级菜单B", 2));
    allMenuList.add(new Menu(3, 1, "二级菜单AA", 1));
    allMenuList.add(new Menu(4, 2, "二级菜单BB", 1));
    allMenuList.add(new Menu(5, 3, "三级菜单AAA", 2));
    allMenuList.add(new Menu(6, 4, "三级菜单BBB", 2));
    allMenuList.add(new Menu(7, 1, "二级菜单AC", 2));

    //  第二种写法
    List<Menu> list = allMenuList.stream()
            .filter(e->e.getPid().equals(0))
            .peek(e->e.setChildren(getChild2(e.getId(), allMenuList)))
            .sorted(Comparator.comparing(Menu::getSort))
            .collect(Collectors.toList());
}

/**
 * 递归查找子菜单(简写版)
 */
public List<Menu> getChild2(Integer pid, List<Menu> allMenuList) {
    return allMenuList.stream()
            .filter(e->pid.equals(e.getPid()))
            .peek(e->e.setChildren(getChild2(e.getId(), allMenuList)))
            .sorted(Comparator.comparing(Menu::getSort))
            .collect(Collectors.toList());
}

 

本文转载于网络 如有侵权请联系删除

相关文章

  • Php公众号40029,网页授权获取微信用户信息错误40029:不合法的oauth_code

    大家好,又见面了,我是你们的朋友全栈君。这几天测试刚完成的网页授权获取微信用户信息功能。在第一步:用户同意授权获取code,通过code获取access_token时,有时会出现40029错误。经过调试,发现问题出现在redirect_uri=REDIRECT_URI当跳转到授权链接后,微信会发出两次转向至redirect_uri的相同请求(两次带进来的code是相同的)。第一次的code后已经成功换取得openid以及access_token;第二次转向到redirect_uri时,该code已经失效(code只能使用一次),从而导致了40029:不合法的oauth_code的错误,不能再获取到access_token。由于面一次被终止,生效的为第二次,因而不能获取到用户信息。(可这种情况只是偶尔发生,过一会儿再进入又正常了),请教这个问题应该如何解决?===========================================================================下面贴一张我获取微信用户信息的流程图,请教各位这个流程是不是存在什么问题呢?改怎么改

  • SuperEdge的新特性和未来之路

    王冬,腾讯云高级研发工程师,专注于Kubernetes、容器等云原生领域,SuperEdge核心开发人员,现负责腾讯云边缘容器TKEEdge私有化相关工作。背景2021年9月27号,在VMware联合了Intel、PingCAP等多家合作公司举办的2021智能云边开源峰会边缘计算专场上,来自腾讯云的高级工程师王冬,发表《SuperEdge的新特性和未来之路》的分享。SuperEdge[1]是2020年由腾讯联合Intel、VMware、虎牙直播、寒武纪、首都在线和美团等多家公司共同发起的边缘计算分布式容器管理系统,旨在将Kubernetes集中式资源管理能力无缝拓展到边缘计算和分布式资源管理场景,统管边缘设备和应用,为众多的IoT设备赋能。以下是分享全文。SuperEdge的四大特性SuperEdge来源 SuperEdge是腾讯云边缘容器管理系统TKEEdge产品族中的一个开源产品,其对应的商业产品是TKEEdge公有云服务和TKEEdge私有化服务。其公有云服务在2018年就在内部进行孵化,2019年正式公测并对外提供服务,目前对外完全免费;其私有化产品目前由灵雀云对外提供整体的交

  • 【干货】Android 一线互联网面试题汇总,13模块200+题,征服面试官不是梦!

    今天分享的面试题,基本都是针对一线互联网公司的面试题整理的,我这里就不单独以公司为单位整理,我自己进行了分类,循序渐进,由基础到深入,由易到简。每个问题的答案我都整理好了一个文档:AndroidBAT面试题整理,这样就可以节省大家自己去搜索的时间,把时间用在正确的东西上。金九银十,需要跳槽的快快复习和准备吧!AndroidBAT面试题目录:1.四大组件 2.Fragment 3.自定义组件、动画 4.存储 5.网络 6.图片 7.布局 8.性能优化 9.JNI 10.进程间通信(简称:IPC) 11.WebView 12.进程保活 13.其他相关面试题1.四大组件四大组件是什么四大组件的生命周期Activity之间的通信方式横竖屏切换的时候,Activity各种情况下的生命周期Activity与Fragment之间生命周期比较Activity上有Dialog的时候按Home键时的生命周期两个Activity之间跳转时必然会执行的是哪几个方法?Activity的四种启动模式对比以及使用场景Activity状态保存与恢复Activity怎么和Service绑定Service和Activit

  • H3C基本命令学习(整理)

    sysname[H3C]sysnameAR2830[AR2830]superpasswordsuperpassword[ level user-level]{simple|cipher}passwordundosuperpassword[leveluser-level]user-level  1-3simple 明文cipher 密文[H3C]superpasswordlevel3simplezbrlocal-userpassword-display-modelocal-userpassword-display-mode{cipher-force|auto}undolocal-userpassword-display-modecipher-force强制方式,即所有接入用户的密码显示方式必须采用密文方式auto自动方式,即接入用户的密码显示方式可以由用户通过password命令来设置[H3C]local-userpassword-display-modecipher-forceradiusschemesystemradiusscheme radius-scheme-nameundora

  • vuex - 辅助函数学习

    官网文档:https://vuex.vuejs.org/zh-cn/api.html  最底部mapState此函数返回一个对象,生成计算属性-当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。mapState可以声明多个需要在组件中引入:import{mapState}from'vuex'复制...mapState({//...}) 对象展开运算符mapGetters将store中的多个getter映射到局部组件的计算属性中组件中引入import{mapGetters}from'vuex'复制组件的computed计算属性中使用1computed:{ 2 3//使用对象展开运算符将getter混入computed对象中 4...mapGetters([ 5 6'doneTodosCount', 7 8'anotherGetter', 9 10//... 11]) 12 13}复制或者给getter属性另起个名字:mapGetters({ doneCount:'done

  • for 循环,while循环,break,continue,exit

    一、for循环常见的两种循环,在脚本中普遍被用到。for循环while循环语法:for变量名in条件;do…;done为了更加方便的上手for循环,讲理论是不足矣理解到位的,所以我们脚本来讲。用for循环来写个1-100的求和。#!/bin/bash sum=0 foriin`seq1100` do sum=$[$sum+$i] echo$i done echo$sum复制思路:首先需要把1-100循环循环一次相加一次,并赋值给sum输出sum的值seq这个就是遍历1-100这些数字。用for循环,遍历一个目录的目录或者文件#!/bin/bash cd/etc///脚本中如果查询的目录在多级目录下,首先要进入到目录,或者,直接跟绝对路径 forain`ls/etc/`//遍历/etc目录 do if[-d$a]//一个一个的去判断是否为目录 then ls-d$a//如果是目录,就查看下目录内的文件+子目录 fi done复制二、while循环语法:while条件; do…; done复制用whiledo写一个死循环。如果说写个脚本去监控系统的负载,我认为while语句最适合不过。

  • 使用selenium+phantomJS实现网页爬取

    有些网站反爬虫技术设计的非常好,很难采用WebClient等技术进行网页信息爬取,这时可以考虑采用selenium+phantomJS模拟浏览器(其实是真实的浏览器)的方式进行信息爬取。之前一直使用的selenium操作Firefox浏览器进行爬取,但是需要安装并打开firefox浏览器,实际操作中不方便配置且占用大量内存。今日发现网上介绍可以采用phantomJS(无界面浏览器),经测试,果然可以达到目的,只是会出现一个控制台,并不影响整体效果,所以将其记录下来,以方便以后使用。第一步:下载selenium-dotnet,选择相应的版本并引用。第二步:下载phantomjs-2.1.1-windows.zip(http://phantomjs.org/download.html),解压下载的文件,将phantomjs.exe文件拷贝到系统目录或者项目的exe目录下。第三步:通过调用phantomjs爬取网页信息,代码如下:1、创建driver实例staticIWebDriverGetPhantomJSDriver() { returnnewOpenQA.Selenium.Phanto

  • 编程实践 | Scala亮瞎Java的眼(一)

    这是我在11月15日成都OpenParty分享的一个题目,确有标题党的嫌疑。Scala自然不是无所不能,Java也没有这么差劲,我只希望给Java程序员提供另外一条可能的选择。在Java8后,我对Java的怨念已经没有那么强烈了,然而,Scala的优势仍然存在。比较Java8,我重点讲解了Scala的如下优势:简洁代码支持OO与FP高阶函数丰富的集合操作Stream支持并发支持 简洁代码Scala提供的脚本特性以及将函数作为一等公民的方式,使得它可以去掉不少在Java中显得冗余的代码,例如不必要的类定义,不必要的main函数声明。Scala提供的类型推断机制,也使得代码精简成为可能。Scala还有一个巧妙的设计,就是允许在定义类的同时定义该类的主构造函数。在大多数情况下,可以避免我们声明不必要的构造函数。Scala还提供了一些非常有用的语法糖,如伴生对象,样例类,既简化了接口,也简化了我们需要书写的代码。例如如下代码:caseclassPerson(name:String,age:Int) vall=List(Person("Jack",28),Person(&qu

  • GC算法

    一。引用计数算法   比较古老的回收算法。原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。   垃圾回收时,只用收集计数为0的对象。此算法最致命的是无法处理循环引用的问题。   两种实现方式   侵入式与非侵入性,引用计数算法的垃圾收集一般有侵入式与非侵入式两种,侵入式的实现就是将引用计数器直接根植在对象内部,用C++的思想进行解释就是,在对象的构造或者拷贝构造中进行加一操作,在对象的析构中进行减一操作,非侵入式恩想就是有一块单独的内存区域,用作引用计数器 图解说明如下:     优点:引用计数器实现简单,效率高 缺点:它很难解决对象之间相互循环引用的问题,同时每次计数器的增加和减少都带来了很多额外的开销,所以在JDK1.1之后,这个算法已经不再使用了。 (对象之间相互循环引用:例如一个父对象持有一个子对象的引用,子对象也持有父对象的引用,这种情况下,父子对象将一直存在于JVM的堆中,无法进行回收)   二。根搜索算法 定义:根搜索方法是通过一些“GCRoots”对象作为起点,从这些节点开始往下搜索,搜索通过的路径成为引用链(Re

  • css3动画从底部自动上升

    效果:红色的字和背景从底部慢慢升起代码<style>.father{width:300px;height:280px;border:1pxsolid#ccc;margin:50pxauto;}.fatherimg{width:300px;height:200px;}.son{width:300px;height:80px;overflow:hidden;}.son1{animation:sport1s;transform:translateY(0px);width:300px;height:80px;background:#cd0200;}@keyframessport{0%{transform:translateY(80px);opacity:1;}25%{transform:translateY(50px);opacity:1}50%{transform:translateY(20px);opacity:1}100%{transform:translateY(0px);opacity:1}}h3,p{color:#000000;font-size:13px;}h3{pa

  • 【自用】Spring Boot使用WebSocket

    引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> 复制 声明SpringBoot需要用WebSocket packagecn.codersy.wiki.config; importorg.springframework.context.annotation.Bean; importorg.springframework.context.annotation.Configuration; importorg.springframework.web.socket.server.standard.ServerEndpointExporter; /** *声明需要使用WebSocket *@authorTenMoons *@date2021-08-1422:35 */ @Configuration public

  • 使用 Composer 为 ThinkPHP(3.2.3)框架添加和管理组件

      使用Composer为ThinkPHP(3.2.3)框架添加和管理组件   环境:Windows1064位 PHP版本:5.5.12 框架:ThinkPHP3.2.3复制 Tips: 组件:打包的代码,可以是一系列相关的类(class)、接口(interface)、特性(trait),用于解决某个具体的问题。组件中的类、接口、特性通常放在同一个命名空间中。 Packagist:https://packagist.org/,该网站收集PHP组件,可以在上面查找项目中需要的组件。ThinkPHP在该站的地址为:https://packagist.org/packages/topthink/thinkphp Composer:Composer是PHP组件的依赖管理器,在命令行中运行,通过运行命令可以下载组件(以及组件的依赖)并且把组件(以及组件的依赖)自动加载到项目中。   安装Composer Composer官网地址:https://getcomposer.org/,中文镜像地址:http://www.phpcomposer.co

  • C++标准库:std_map作为一个关联数组

      摘要:std::map作为一个容器存在一个典型应用就是作为关联数组来作用。在诸如Java等等语言中,关联数组广泛存在。std::map是一个容器,在它的概念框架中存在两个词:键和值,std::map把一个键与一个值相对,它相当于一个字典,把一个索引和一人内容对应起来。一般情况下,std::map用一个平衡二叉树来实现的,所以它的大部分操作都可以log(n)的时间里完成。下面让我们看看其中的简单使用: #include<map>//定义一个整数到整数的映射//其中前一个键,后一个是值std::map<int, int> map;//要创建一个简单的映射,可以这样//下面两句在map创建两个结点,分别完成预期的映射map[0]=12;map[99]=13;   std::map是以结点的形式存储其中的元素的,我们可以手工制造一个结点然后放到容器中或者从容器删除相关条件的结点,这些都由“键”来驱动的,也就是说一个操作你往往要告诉std::map是针对哪个键进行的。 下面给出一些典型的代码: #include<

  • windows 10玩mysql 8

    注意事项: 1)windows10只支持两个版本:5.7,8.0 2)安装有两种方式,zip与installer,建议用zip方式,因为installer要安装许多依赖,如visionc++等,较为烦琐   #OS版本适配查询 https://www.mysql.com/support/supportedplatforms/database.html   #安装文件下载 https://dev.mysql.com/downloads/mysql/   #安装教程 https://www.cnblogs.com/zhangkanghui/p/9613844.html  zipformysql5.7,8.0OK

  • LightGBM算法实践

    LightGBM是Boosting算法的一种,与GBDT、XGBOOST是属于同一类算法,很多情况下可能会需要lightGBM与GBDT、xgb进行比较。这里花点时间简单比较一下: 一、GBDT   GBDT是通过使用回归树来构建每一个弱分类器,(具体为啥使用回归树的原因是因为GBDT是由于每次迭代都是拟合上一颗树的残差(一阶梯度且是负梯度),最后所有的树结果求和即使最后结果。只有回归树才可以实现求和,那这里还会涉及到如何构建回归树,以及特征分裂点的选择的方法及其比较。相关的衍生内容很多,这里暂且点到为止)。   优点:   (1)可以灵活处理各种类型的数据,包括连续值和离散值。   (2)在相对少的调参时间情况下,预测的准确率也可以比较高。这个是相对SVM来说的。   (3)使用一些健壮的损失函数,对异常值的鲁棒性非常强。比如Huber损失函数和Quantile损失函数。   缺点:   (1)训练过程需要串行训练,只能在决策树内部采用一些局部并行的手段提高训练速度。 二、XGBOOST   XGB是在GBDT的基础上做了一些优化和改进,主要是(1)损失函数是二阶泰勒展开,相比GBD

  • Optional避免null指针

    Useruser=Optional.ofNullable(user1).filter(e->Objects.nonNull(e.getName())&&Objects.nonNull(e.getAge())).orElseThrow(()->newCodeException(Code.FAILURE_USER_NOT_EXIST));

  • 链接Caffe,程序报错应用程序无法正常启动(0xc000007b)

    目录背景Debug解决办法原因(猜想)总结 重点是介绍了一种排查这个问题的方法。 背景 Windows下,Caffe单独编译成库并且安装在路径Caffe_DIR,动态链接库Caffe_DIR/bin已经加入环境变量了 Run_DIR/main.exe是一个链接了Caffelib的程序,在运行的时候报错“应用程序无法正常启动(0xc000007b),请单击“确定”关闭应用程序” Debug 把main.exe复制到Caffe_DIR/bin中(和一堆dll放一起),双击运行,嗯?可以跑;这说明问题出在没有找到必要的.dll,但是我明明把Caffe_DIR加入环境变量并且重启过啊,不明所以,继续试验 把Caffe_DIR/bin里面所有.dll复制到原先的main.exe所在目录Run_DIR,双击main.exe,可以跑,确定问题就出在这堆.dll中 (前提是Caffe_DIR/bin已经在环境变量中)逐个删除Run_DIR里的.dll文件,每删除一个就运行一下main.exe,直到在删除libgcc_s_seh-1.dll之前都可以运行(这也证明了我们之前加入环境变量

  • 同一个电脑使用多份密钥文件绑定多个帐号

    解决同一台电脑生成两份或多份ssh密钥、公钥映射两个或多个GitHub账号 需求描述 本人注册一个GitHub账户,用来分享本人自己的开源项目或者代码,同时,公司注册了一个GitHub账户,用来分享公司的开源项目。如果按照单个ssh公钥生成的方法则会把之前的公钥覆盖掉,这样将导致其中一方在下一次上传代码,本机和GitHub无法映射成功。 需求分析 解决这个问题首先要明确如何生成单个ssh公钥。 ssh生成单个公钥命令:ssh-keygen-trsa-b4096-C"your_email@example.com"。如何生成ssh公钥 上述命令会在当前~/.ssh目录下生成id_rsa和id_rsa.pub两个文件。其中id_rsa是私钥文件,id_rsa_.pub是公钥文件。 id_rsa和id_rsa_.pub文件都是通过一个邮箱号生成的,同一个公钥文件不可以配置两个不同GitHub账户(已测试)。 那么两个GitHub账户就需要两个不同的邮箱号,来生成两组不同的公钥文件。 解决方案 分别指定ssh公钥文件的名称即可。 命令:ssh-keygen-trsa-C"your_email

  • 1、redis安装与启动

    1、安装包下载 官网上下载:http://www.redis.io/ 安装版本:3.0.7 安装环境:CentOS 下载命令:wget http://download.redis.io/releases/redis-3.0.7.tar.gz(进入官网download页面,具体版本上右键复制下载地址)   2、安装步骤 1$wgethttp://download.redis.io/releases/redis-3.0.7.tar.gz 2$tarxzfredis-3.0.7.tar.gz 3$cdredis-3.0.7 4$make复制 make完成以后,编译得到的二进制文件放在src文件夹下面,可以简单的执行下面的命令来启动redis服务: $src/redis-server复制 启动redis-cli客户端,便可以连接上redis服务: $src/redis-cli复制   3、检测 检测一下redis系统是否正常,在redis-3.0.7目录中执行maketest。执行该命令之前要保证系统安装了ctl8.5(或更高的版本)。先安装一下ctl

  • HDU3693 Math Teacher&#39;s Homework ---- 数位DP

    HDU3693MathTeacher'sHomework 一句话题意 给定$n,k以及m_1,m_2,m_3,...,m_n$求$x_1\oplusx_2\oplusx_3\oplus...\oplusx_n==K(x_1\leqm_1,x_2\leqm_2...)$的方案数。   题解 一开始口糊了一下,然后写代码的时候发现不少东西没考虑周到,于是就看起了题解。 我们首先需要发现一个重要的性质: 如果某一位上不受m限制(也就是选0或选1都可以)那么无论其它数的这一位位选什么都可以通过这一位来变成结果和K的这一位相等 为了避免来自$x<=m$的麻烦,我们首先让$m++$,使条件变为$x<m$。 然后按照数位dp的套路对位分析。 首先假设我们处理到了第j位,然后从高位到$j+1$位都已经到了最大值 然后第$j$位由于要小于m,所以m的这一位必然1,然后j的这一位必然是0 然后按照套路我们发现如果这一位我们选了0,那么后面的位随便选都不会大于m 为了方便dp,我们令每一位最早允许随便选的那个$x_i$为$A_j$,这个数将在后面被限制以使其它自由位达到K上对应位的要求

  • SQL Server开启READ_COMMITTED_SNAPSHOT

    按照网上搜索的下面 ------------------------------------------------------------------------------- 设置数据库为SINGLE_USER模式,减少锁定时间 ALTERDATABASEdbnameSETSINGLE_USERWITHROLLBACKIMMEDIATE ALTERDATABASEdbnameSETALLOW_SNAPSHOT_ISOLATIONON ALTERDATABASEdbnameSETREAD_COMMITTED_SNAPSHOTON ALTERDATABASEdbnameSETMULTI_USER 开启之后一天了没有看到死锁的记录,还是有点作用的! -------------------------------------------------------------------------------   没有仔细看这句 ALTERDATABASEdbnameSETSINGLE_USERWITHROLLBACKIMMEDIATE   直接把生产环境数据库干死了。毫

相关推荐

推荐阅读