这里是数据库左右值结构的设计
https://my.oschina.net/wuzhaohui/blog/113194
找了很多左右值的文章,全部都是讲数据库的,但最终要返回给前端的树形结构却没有,便自己试着写了一个,
数据库结构和数据
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for menu
-- ----------------------------
DROP TABLE IF EXISTS `menu`;
CREATE TABLE `menu` (
`id` int(10) NOT NULL,
`menuName` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`lft` int(10) NULL DEFAULT NULL,
`rgt` int(10) NULL DEFAULT NULL,
`level` int(10) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of menu
-- ----------------------------
INSERT INTO `menu` VALUES (1, '主菜单', 1, 30, 1);
INSERT INTO `menu` VALUES (2, '子菜单1', 2, 9, 2);
INSERT INTO `menu` VALUES (3, '子子菜单1', 3, 8, 3);
INSERT INTO `menu` VALUES (4, '子子子菜单1', 4, 7, 4);
INSERT INTO `menu` VALUES (5, '子菜单3', 10, 15, 2);
INSERT INTO `menu` VALUES (6, '子子子子菜单1', 5, 6, 5);
INSERT INTO `menu` VALUES (7, '子子菜单3', 11, 14, 3);
INSERT INTO `menu` VALUES (8, '子子子菜单3', 12, 13, 4);
INSERT INTO `menu` VALUES (9, '子菜单2', 16, 19, 2);
INSERT INTO `menu` VALUES (10, '子菜单4', 22, 29, 2);
INSERT INTO `menu` VALUES (11, '子子菜单4', 23, 28, 3);
INSERT INTO `menu` VALUES (12, '子子子菜单4', 24, 25, 4);
INSERT INTO `menu` VALUES (13, '子子菜单2', 17, 18, 3);
INSERT INTO `menu` VALUES (14, '子菜单5', 20, 21, 2);
INSERT INTO `menu` VALUES (15, '子子子菜单4-1', 26, 27, 4);
SET FOREIGN_KEY_CHECKS = 1;
代码如下
// 实体类
@Data
@TableName("menu")
public class Menu implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@TableField("menuName")
private String menuName;
@TableField("lft")
private Integer lft;
@TableField("rgt")
private Integer rgh;
@TableField("level")
private Integer level;
@TableField(exist=false)
private List<Menu> list;
}
数据递归方法
public void treeStructure(Menu menuParent, List<Menu> menuList) {
for (Menu son : menuList) {
// 查找根节点下子节点 子节点的左值 - 1 == 根节点的左值 或者 子节点右值 + 1 == 根节点右值
// 例如 1 - 6
// 2 - 3 4 - 5
if (son.getLft() - 1 == menuParent.getLft() || son.getRgh() + 1 == menuParent.getRgh()) {
// 这里先判断根节点的子节点是否有值,没有赋值, 有了就添加 直接赋值会覆盖之前的节点
if (menuParent.getList() == null) {
menuParent.setList(new ArrayList<>(Collections.singletonList(son)));
} else {
menuParent.getList().addAll(new ArrayList<>(Collections.singletonList(son)));
}
}
// 这里是判断 特殊节点的 如下
// 1 - 12
// 2 - 5 6 - 9 10 - 11
// 3 - 4 7 - 8
// 这里的 6 - 7 左值 - 1 != 根节点的左值 右值 + 1 != 根节点右值
// 这里的if判断是防止出现 7 - 8 被放置在 2 - 3 节点下
else if ((menuParent.getRgh() > son.getRgh() && menuParent.getLft() < son.getLft())) {
for (Menu menu : menuParent.getList()) {
// 循环根节点的子节点 保证他们的层级是一样的 该节点的左值 大于其他节点的右值 或者 该节点的右值小于其他节点的左值
// 2 - 5 6 - 9 10 - 11 6 - 9 左值大于 2-5 的右值 右值小于 10 - 11 的左值
boolean b1 = (menu.getRgh() < son.getLft() || menu.getLft() > son.getRgh())
&& menu.getLevel().equals(son.getLevel());
if (b1) {
// 进入这个方法 就不用判断 list 是否为null 上面的判断已经添加了,如果这里报空 就该检查是否左右值错误
menuParent.getList().addAll(new ArrayList<>(Collections.singletonList(son)));
// 这里结束本次循环,防止重复加入
break;
}
}
}
}
// 这里递归调用方法, 把根节点的子节点再次当成根节点传入方法
if (menuParent.getList() != null) {
for (Menu menu : menuParent.getList()) {
treeStructure(menu, menuList);
}
}
}
数据结构
{
"id": 1,
"menuName": "主菜单",
"lft": 1,
"rgh": 30,
"level": 1,
"list": [
{
"id": 2,
"menuName": "子菜单1",
"lft": 2,
"rgh": 9,
"level": 2,
"list": [
{
"id": 3,
"menuName": "子子菜单1",
"lft": 3,
"rgh": 8,
"level": 3,
"list": [
{
"id": 4,
"menuName": "子子子菜单1",
"lft": 4,
"rgh": 7,
"level": 4,
"list": [
{
"id": 6,
"menuName": "子子子子菜单1",
"lft": 5,
"rgh": 6,
"level": 5,
"list": null
}
]
}
]
}
]
},
{
"id": 5,
"menuName": "子菜单3",
"lft": 10,
"rgh": 15,
"level": 2,
"list": [
{
"id": 7,
"menuName": "子子菜单3",
"lft": 11,
"rgh": 14,
"level": 3,
"list": [
{
"id": 8,
"menuName": "子子子菜单3",
"lft": 12,
"rgh": 13,
"level": 4,
"list": null
}
]
}
]
},
{
"id": 9,
"menuName": "子菜单2",
"lft": 16,
"rgh": 19,
"level": 2,
"list": [
{
"id": 13,
"menuName": "子子菜单2",
"lft": 17,
"rgh": 18,
"level": 3,
"list": null
}
]
},
{
"id": 14,
"menuName": "子菜单5",
"lft": 20,
"rgh": 21,
"level": 2,
"list": null
},
{
"id": 10,
"menuName": "子菜单4",
"lft": 22,
"rgh": 29,
"level": 2,
"list": [
{
"id": 11,
"menuName": "子子菜单4",
"lft": 23,
"rgh": 28,
"level": 3,
"list": [
{
"id": 12,
"menuName": "子子子菜单4",
"lft": 24,
"rgh": 25,
"level": 4,
"list": null
},
{
"id": 15,
"menuName": "子子子菜单4-1",
"lft": 26,
"rgh": 27,
"level": 4,
"list": null
}
]
}
]
}
]
}