Java后端将查询数据组装成树结构

本文介绍如何在Java中构建一个物料分类的数据树结构。通过自关联查询获取所有节点,然后递归组装成树形结构,其中最高级父节点的标识为0。查询最高父节点的SQL语句被展示,并且使用了Linq库来过滤子节点。最后,给出了数据组装的效果和所需引入的Linq依赖。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

场景与实现:一张表的自关联存储,后台一次性查询出所有数据(所有节点数据与最高级父节点数据),

注:再进行组装。这里的最高级父节点的materialtypeParent默认为0。

1、实体类

实体类为BasMaterialType,继承了树公共类TreeEntity,树公共类继承了实体公共字段类BaseDomain。(存在部分注解或字段非组件所必须字段,为了完整性全部CV上来)

  • BasMaterialType
/**
 * 物料分类维护对象 bas_material_type
 * 
 * @author ruoyi
 * @date 2021-04-27
 */
@Data
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@Accessors(chain = true)
@TableName("bas_material_type")
public class BasMaterialType extends TreeEntity
{

    /** 分类主键 */
    @TableId(value = "materialtype_id")
    @JsonFormat(shape = JsonFormat.Shape.STRING)
    private Long materialtypeId;
    
    /** 分类编码 */
    @Excel(name = "分类编码")
    private String materialtypeCode;
    
    /** 分类名称 */
    @Excel(name = "分类名称")
    private String materialtypeName;
    
    /** 分类父级 */
    @Excel(name = "分类父级")
    @JsonFormat(shape = JsonFormat.Shape.STRING)
    private Long materialtypeParent;
    
    /** 分类项次 */
    @Excel(name = "分类项次")
    private Long materialtypeSeq;

}
  • TreeEntity
/**
 * Tree基类
 * 
 * @author ruoyi
 */
public class TreeEntity extends BaseDomain
{
    private static final long serialVersionUID = 1L;

    /** 父菜单名称 */
    @TableField(exist = false)
    private String parentName;

    /** 父菜单ID */
    @TableField(exist = false)
    private Long parentId;

    /** 显示顺序 */
    @TableField(exist = false)
    private Integer orderNum;

    /** 祖级列表 */
    @TableField(exist = false)
    private String ancestors;

    /** 子部门 */
    @TableField(exist = false)
    private List<?> children = new ArrayList<>();

    public String getParentName()
    {
        return parentName;
    }

    public void setParentName(String parentName)
    {
        this.parentName = parentName;
    }

    public Long getParentId()
    {
        return parentId;
    }

    public void setParentId(Long parentId)
    {
        this.parentId = parentId;
    }

    public Integer getOrderNum()
    {
        return orderNum;
    }

    public void setOrderNum(Integer orderNum)
    {
        this.orderNum = orderNum;
    }

    public String getAncestors()
    {
        return ancestors;
    }

    public void setAncestors(String ancestors)
    {
        this.ancestors = ancestors;
    }

    public List<?> getChildren()
    {
        return children;
    }

    public void setChildren(List<?> children)
    {
        this.children = children;
    }
}
  • BaseDomain
/**
 * @author Administrator
 */
@Data
@ToString
@NoArgsConstructor
public class BaseDomain {

    /**
     * 创建人
     */
    @TableField(value = "created_user", fill = FieldFill.INSERT)
    private String createdUser;

    /**
     * 创建时间
     */
    @TableField(value = "created_time", fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createdTime;

    /**
     * 创建人名
     */
    @TableField(value = "created_name", fill = FieldFill.INSERT)
    private String createdName;

    /**
     * 修改人名
     */
    @TableField(value = "updated_user", fill = FieldFill.INSERT_UPDATE)
    private String updatedUser;

    /**
     * 修改时间
     */
    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updatedTime;

    /**
     * 修改人名
     */
    @TableField(value = "updated_name", fill = FieldFill.INSERT_UPDATE)
    private String updatedName;

    @Version
    @TableField(value = "revision",fill = FieldFill.INSERT)
    private Integer revision;

    /**
     * 备注信息
     */
    @TableField(value = "remark")
    private String remark;

}

2、数据树组装

直接来到组装,其他controller、service、mapper就不写了,提供组装和SQL(查询最高父节点)

  • 组装树形结构

    /**
     * 获取物料分类树结构
     * @return
     */
    @Override
    public List<BasMaterialType> getDischargeTree() {
    	// 查询所有节点
        List<BasMaterialType> basDischargeInfoList = this.list();
        // 查询最高父节点
        List<BasMaterialType> rootNode = basMaterialTypeMapper.selectDischargeTree();
        // 设置子节点
        rootNode.forEach(basDischargeInfo -> {
            basDischargeInfo.setChildren(this.getChildren(basDischargeInfoList,basDischargeInfo.getMaterialtypeId()));
        });
        return rootNode;
    }

    /**
     * 获取根据父节点ID获取子节点
     * @param allNode
     * @param parentId
     * @return
     */
    public List<BasMaterialType> getChildren(List<BasMaterialType> allNode, Long parentId){
    	// 使用Linq匹配出传入父节点的下一级子节点
        List<BasMaterialType> basDischargeInfoList = Linq.of(allNode).where(item->item.getMaterialtypeParent().equals(parentId)).toList();
        /**
         * 2023.02.03 更新
         * 这里可以用流处理,省去引入依赖
         */
         // List<BasMaterialType> basDischargeInfoList = allNode.stream().filter(item -> item.getMaterialtypeParent().equals(parentId)).collect(Collectors.toList());
        if (basDischargeInfoList.size()==0){
            return new ArrayList<>();
        }
        //递归下一级
        basDischargeInfoList.forEach(basDischargeInfo -> {
            basDischargeInfo.setChildren(getChildren(allNode,basDischargeInfo.getMaterialtypeId()));
        });
        return basDischargeInfoList;
    }
  • 查询最高父节点SQL
select 
	materialtype_id, 
	materialtype_code, 
	materialtype_name, 
	materialtype_parent, 
	materialtype_seq, 
	revision, 
	created_user, 
	created_name, 
	created_time, 
	updated_user, 
	updated_name, 
	updated_time, 
	remark 
from bas_material_type
where materialtype_parent = 0

3、组装效果

在这里插入图片描述
在这里插入图片描述

4、后言

  • 使用Linq需要导入的依赖
<!-- Linq -->
<dependency>
    <groupId>com.bestvike</groupId>
    <artifactId>linq</artifactId>
    <version>3.1.0</version>
    <scope>compile</scope>
</dependency>
<think>好的,用户想在RuoYi框架中实现树形表格的功能,我需要先回忆一下RuoYi的结构。RuoYi是基于Spring Boot和MyBatis的后台管理系统,前端通常用Thymeleaf或者Vue,树形表格一般用于展示层级数据,比如部门结构或者菜单树。 首先,用户可能需要数据库表设计方面的指导。树形结构通常需要父节点ID字段,比如parent_id,这样每个记录可以指向其父级。例如,部门表会有dept_id、parent_id、dept_name等字段。这一步是基础,必须确保数据模型正确。 接下来是后端部分。RuoYi的后端控制器需要提供接口来查询树形数据。可能需要一个服务层的方法,使用递归或者MyBatis的嵌套查询来构建树形结构。例如,通过递归查询每个节点的子节点,这里要注意避免N+1查询问题,可能需要一次性获取所有节点再组装树,或者使用MyBatis的collection标签进行嵌套结果映射。 然后是前端部分。RuoYi的前端如果用的是Vue和Element UI,那么可以使用Element的树形表格组件,比如<el-table>结合<el-tree>的特性,或者使用第三方组件如vue-treetable。需要将后端返回的树形数据格式化,确保每个节点有children属性,这样组件才能正确渲染层级。 示例代码部分,可能需要给出后端Controller的示例,返回树形结构的JSON数据,以及前端Vue组件中如何配置表格,特别是如何绑定树形数据。同时,可能需要处理展开和折叠的功能,以及样式的调整,比如缩进层级。 另外,用户可能需要知道如何优化性能,比如懒加载子节点,当展开时再请求数据,而不是一次性加载所有节点。这对于大数据量的树形结构很重要。此外,添加、编辑和删除节点后如何刷新表格,保持数据的一致性也是需要考虑的。 常见的问题可能包括数据循环引用导致的无限递归,或者前端组件配置不正确导致树形结构无法显示。需要提醒用户检查数据的parent_id是否正确,避免形环。同时,后端查询是否正确地组装了子节点列表。 最后,相关的问题可能会涉及如何实现节点的拖拽排序、权限控制下的树形显示,或者如何导出树形表格数据为Excel等。需要准备好这些扩展点的简要回答,帮助用户进一步探索。</think>### RuoYi框架树形表格实现方法 在RuoYi框架中实现树形表格需要结合后端数据结构和前端组件配置,以下是具体步骤: --- #### 一、数据库表设计 树形结构数据表需包含以下字段: ```sql CREATE TABLE sys_dept ( dept_id BIGINT PRIMARY KEY, parent_id BIGINT DEFAULT 0, dept_name VARCHAR(50), order_num INT ); ``` * `parent_id` 表示父节点ID,根节点设为0 * `order_num` 控制同级节点排序 --- #### 二、后端实现(Java) 1. **实体类添加`children`字段** ```java public class SysDept extends BaseEntity { private Long deptId; private Long parentId; private String deptName; private List<SysDept> children; // getter/setter } ``` 2. **构建树形结构工具类** ```java public class TreeUtils { public static <T extends BaseEntity> List<T> buildTree(List<T> nodes) { List<T> tree = new ArrayList<>(); Map<Long, T> nodeMap = nodes.stream().collect(Collectors.toMap(T::getId, node -> node)); for (T node : nodes) { if (node.getParentId() == 0) { tree.add(node); } else { T parent = nodeMap.get(node.getParentId()); if (parent != null) { parent.getChildren().add(node); } } } return tree; } } ``` 3. **Controller接口** ```java @GetMapping("/listTree") public AjaxResult listTree(SysDept dept) { List<SysDept> list = deptService.selectDeptList(dept); return AjaxResult.success(TreeUtils.buildTree(list)); } ``` --- #### 三、前端实现(Vue + Element UI) 1. **安装树形表格组件** ```bash npm install vue-treetable --save ``` 2. **组件配置** ```vue <template> <treetable :data="deptList" :columns="columns" :expand-keys="expandKeys" @expand-change="handleExpand" > <template #deptName="{ row }"> <span :style="{ paddingLeft: (row.level * 15) + 'px' }"> {{ row.deptName }} </span> </template> </treetable> </template> <script> import Treetable from 'vue-treetable' export default { components: { Treetable }, data() { return { columns: [ { label: '部门名称', prop: 'deptName' }, { label: '排序', prop: 'orderNum' } ], deptList: [] } }, created() { this.getDeptTree() }, methods: { getDeptTree() { axios.get('/system/dept/listTree').then(res => { this.deptList = this.formatTreeData(res.data) }) }, formatTreeData(data, level = 0) { return data.map(item => ({ ...item, level, children: item.children?.length ? this.formatTreeData(item.children, level + 1) : null })) } } } </script> ``` --- #### 四、关键配置说明 1. **后端注意事项** - 避免循环引用问题 - 使用`@JsonInclude(JsonInclude.Include.NON_EMPTY)`过滤空子节点 - 添加`@Accessors(chain = true)`支持链式调用 2. **前端优化点** - 添加懒加载功能 ```js :load="loadChildren" async loadChildren(row, resolve) { const res = await axios.get(`/system/dept/children/${row.deptId}`) resolve(this.formatTreeData(res.data, row.level + 1)) } ``` - 支持拖拽排序 ```vue <treetable v-draggable="deptList"> ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值