数据库树形结构查询

数据库树形结构查询

Oracle实现方式

数据库树形结构,正反遍历

--从Root往树末梢递归

select level ,identity,pid from table_name

start with identity=475

connect by prior identity = pid

--从末梢往树ROOT递归

select level ,identity,pid,yylevel from table_name

start with identity=542

connect by prior pid = identity

作用:

connect by主要用于父子,祖孙,上下级等层级关系的查询

语句:

{ CONNECT BY [ NOCYCLE ] condition [AND condition]... [ START WITH condition ]
| START WITH condition CONNECT BY [ NOCYCLE ] condition [AND condition]...}

解释:

start with: 指定起始节点的条件

connect by: 指定父子行的条件关系

prior: 查询父行的限定符,格式: prior column1 = column2 or column1 = prior column2 and ... ,

nocycle: 若数据表中存在循环行,那么不添加此关键字会报错,添加关键字后,便不会报错,但循环的两行只会显示其中的第一条

循环行: 该行只有一个子行,而且子行又是该行的祖先行

connect_by_iscycle: 前置条件:在使用了nocycle之后才能使用此关键字,用于表示是否是循环行,0表示否,1 表示是

connect_by_isleaf: 是否是叶子节点,0表示否,1 表示是

level: level伪列,表示层级,值越小层级越高,level=1为层级最高节点

 

MySQL实现方式

MySQL没有提供类似函数,只能通过在程序或存储过程中利用递归的方式进行实现

程序代码递归的方式构建树

定义菜单类

public class Menu {

    // 菜单id

    private String id;

    // 菜单名称

    private String name;

    // 父菜单id

    private String parentId;

    // 菜单url

    private String url;

    // 菜单图标

    private String icon;

    // 菜单顺序

    private int order;

    // 子菜单

    private List<Menu> childMenus;

    // ... 省去getter和setter方法以及toString方法

}

 

根据这个类定义数据库,并插入菜单数据

DROP TABLE IF EXISTS `jrbac_menu`;

CREATE TABLE `jrbac_menu` (

`id` varchar(32) NOT NULL COMMENT '主键id,uuid32位',

`name` varchar(64) NOT NULL COMMENT '菜单名称',

`parent_id` varchar(32) DEFAULT NULL COMMENT '父菜单id',

`url` varchar(64) DEFAULT NULL COMMENT '访问地址',

`icon` varchar(32) DEFAULT NULL COMMENT '菜单图标',

`order` tinyint(4) DEFAULT '0' COMMENT '菜单顺序',

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='菜单表';

 

-- ----------------------------

-- Records of jrbac_menu

-- ----------------------------

INSERT INTO `jrbac_menu` VALUES ('1', 'Forms', null, 'forms.html', 'fa fa-edit', '0');

INSERT INTO `jrbac_menu` VALUES ('2', 'UI Elements', null, '', 'fa fa-wrench', '1');

INSERT INTO `jrbac_menu` VALUES ('3', 'Buttons', '2', 'buttons.html', '', '0');

INSERT INTO `jrbac_menu` VALUES ('4', 'Icons', '2', 'icons.html', null, '1');

INSERT INTO `jrbac_menu` VALUES ('5', 'Multi-Level Dropdown', '', '', 'fa fa-sitemap', '2');

INSERT INTO `jrbac_menu` VALUES ('6', 'Second Level Item', '5', 'second.html', null, '0');

INSERT INTO `jrbac_menu` VALUES ('7', 'Third Level', '5', null, '', '1');

INSERT INTO `jrbac_menu` VALUES ('8', 'Third Level Item', '7', 'third.html', null, '0');

 

为了演示,我们把可展开的没有做完,仅仅插入几条数据能出效果就可以了。

 

测试方法与递归方法

private final Gson gson = new GsonBuilder().disableHtmlEscaping().create();

@Test

public void testQueryMenuList() {

    // 原始的数据

    List<Menu> rootMenu = menuDao.queryMenuList(null);

 

    // 查看结果

    for (Menu menu : rootMenu) {

        System.out.println(menu);

    }

    // 最后的结果

    List<Menu> menuList = new ArrayList<Menu>();

    // 先找到所有的一级菜单

    for (int i = 0; i < rootMenu.size(); i++) {

        // 一级菜单没有parentId

        if (StringUtils.isBlank(rootMenu.get(i).getParentId())) {

            menuList.add(rootMenu.get(i));

        }

    }

    // 为一级菜单设置子菜单,getChild是递归调用的

    for (Menu menu : menuList) {

        menu.setChildMenus(getChild(menu.getId(), rootMenu));

    }

    Map<String,Object> jsonMap = new HashMap<>();

    jsonMap.put("menu", menuList);

    System.out.println(gson.toJson(jsonMap));

 

}

 

/**

* 递归查找子菜单

*

* @param id

* 当前菜单id

* @param rootMenu

* 要查找的列表

* @return

*/

private List<Menu> getChild(String id, List<Menu> rootMenu) {

    // 子菜单

    List<Menu> childList = new ArrayList<>();

    for (Menu menu : rootMenu) {

        // 遍历所有节点,将父菜单id与传过来的id比较

        if (StringUtils.isNotBlank(menu.getParentId())) {

            if (menu.getParentId().equals(id)) {

                childList.add(menu);

            }

        }

    }

    // 把子菜单的子菜单再循环一遍

    for (Menu menu : childList) {// 没有url子菜单还有子菜单

        if (StringUtils.isBlank(menu.getUrl())) {

            // 递归

            menu.setChildMenus(getChild(menu.getId(), rootMenu));

        }

    } // 递归退出条件

    if (childList.size() == 0) {

        return null;

    }

    return childList;

}

 

menuDao.queryMenuList(null);查找的结果是一条一条的数据

 

meuDao

package com.jrbac.dao;

 

import java.util.List;

 

import com.jrbac.entity.LoginUser;

import com.jrbac.entity.Menu;

 

public interface MenuDao {

 

    /**

     * 查找用户的菜单

     * @param loginUser

     * @return

     */

    public List<Menu> queryMenuList(LoginUser loginUser);

}

 

 

mybatis

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper

PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.jrbac.dao.MenuDao">

    <select id="queryMenuList" resultType="Menu">

        SELECT

            id,`name`,parent_id,url,icon,`order`

        FROM

            jrbac_menu ORDER BY `order` ASC

    </select>

</mapper>

 

 

利用存储过程

创建表

SET FOREIGN_KEY_CHECKS=0;

 

-- ----------------------------

-- Table structure for t_areainfo

-- ----------------------------

DROP TABLE IF EXISTS `t_areainfo`;

CREATE TABLE `t_areainfo` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`level` int(11) DEFAULT NULL,

`name` varchar(255) DEFAULT NULL,

`parentId` int(11) DEFAULT NULL,

`status` int(11) DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=65 DEFAULT CHARSET=utf8;

 

-- ----------------------------

-- Records of t_areainfo

-- ----------------------------

INSERT INTO `t_areainfo` VALUES ('1', '0', '中国', '0', '0');

INSERT INTO `t_areainfo` VALUES ('2', '0', '华北区', '1', '0');

INSERT INTO `t_areainfo` VALUES ('3', '0', '华南区', '1', '0');

INSERT INTO `t_areainfo` VALUES ('4', '0', '北京', '2', '0');

INSERT INTO `t_areainfo` VALUES ('5', '0', '海淀区', '4', '0');

INSERT INTO `t_areainfo` VALUES ('6', '0', '丰台区', '4', '0');

INSERT INTO `t_areainfo` VALUES ('7', '0', '朝阳区', '4', '0');

INSERT INTO `t_areainfo` VALUES ('8', '0', '北京XX区1', '4', '0');

INSERT INTO `t_areainfo` VALUES ('9', '0', '北京XX区2', '4', '0');

INSERT INTO `t_areainfo` VALUES ('10', '0', '北京XX区3', '4', '0');

INSERT INTO `t_areainfo` VALUES ('11', '0', '北京XX区4', '4', '0');

INSERT INTO `t_areainfo` VALUES ('12', '0', '北京XX区5', '4', '0');

INSERT INTO `t_areainfo` VALUES ('13', '0', '北京XX区6', '4', '0');

INSERT INTO `t_areainfo` VALUES ('14', '0', '北京XX区7', '4', '0');

INSERT INTO `t_areainfo` VALUES ('15', '0', '北京XX区8', '4', '0');

INSERT INTO `t_areainfo` VALUES ('16', '0', '北京XX区9', '4', '0');

INSERT INTO `t_areainfo` VALUES ('17', '0', '北京XX区10', '4', '0');

INSERT INTO `t_areainfo` VALUES ('18', '0', '北京XX区11', '4', '0');

 

 

 

存储过程创建

 

FIND_IN_SET函数说明

FIND_IN_SET(str,strlist)

str 要查询的字符串
strlist 字段名 参数以","分隔 如 (1,2,6,8)
查询字段(strlist)中包含(str)的结果,返回结果为null或记录

 

select * from treenodes where FIND_IN_SET(id, '1,2,3,4,5'); 

使用find_in_set函数一次返回多条记录 

id 是一个表的字段,然后每条记录分别是id等于1,2,3,4,5的时候 

有点类似in (集合) 

select * from treenodes where id in (1,2,3,4,5);

 

 

调用方式

select queryChildrenAreaInfo(2);

select * from t_areainfo where FIND_IN_SET(id, queryChildrenAreaInfo(2));

 

原文地址:https://www.cnblogs.com/kexinxin/p/11749825.html