JAVA树结构组织-PG树结构数据查询
在Java应用中处理树形结构数据,特别是使用PostgreSQL数据库进行树形数据查询和组织的方法。为Java开发者提供了处理树形数据的实用解决方案,特别适合需要在应用中展示和操作树形结构的场景。
主要内容
-
数据结构设计:
- 定义了
SysOrg类,包含id、name、parentId等字段 - 添加了
children字段用于存储子节点,hasChild表示是否有子节点
- 定义了
-
SQL查询实现:
- 使用PostgreSQL的递归查询功能(WITH RECURSIVE)
- 从指定组织ID开始,递归查询所有子节点
- 支持按名称进行模糊查询
-
树形结构组装:
- 通过Map高效建立节点索引和子节点映射
- 遍历所有节点,为每个节点设置children和hasChild属性
- 采用O(n)时间复杂度的算法,避免递归嵌套
-
测试实现:
- 提供了Spring Boot的测试代码
- 展示了如何查询组织树并组装成树形结构
应用场景
该方法适用于需要处理树形结构数据的场景,如:
- 组织架构管理
- 分类目录系统
- 地理行政区划
- 产品分类
优势
- 使用PostgreSQL的递归查询,避免了在Java中进行递归查询的性能问题
- 采用Map高效组装树形结构,时间复杂度为O(n)
- 代码结构清晰,易于维护和扩展
一、数据结构
@Data
@Accessors(chain = true)
@TableName(value = "sys_org", autoResultMap = true)
public class SysOrg implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
private Long id;
/**
* 组织名称
*/
private String name;
/**
* 父级id
*/
private Long parentId;
/**
* 树层级 默认从1开始 表示根组织
*/
private Integer treeLevel;
/**
* 树code
*/
private String treeCode;
/**
* 排序 默认从1开始 表示本层级第一个
*/
private Integer sort;
/**
* 层级
*/
private Integer regionLevel;
/**
* 区划code
*/
private String regionCode;
/**
* gis平台的系统id
*/
private String gisSysId;
/**
* 创建人
*/
private Long createUserId;
/**
* 创建时间
*/
private LocalDateTime createDateTime;
/**
* 更新人
*/
private Long updateUserId;
/**
* 更新时间
*/
private LocalDateTime updateDateTime;
@TableField(exist = false)
private List<SysOrg> children;
/**
* 是否有子机构
*/
@TableField(exist = false)
private Boolean hasChild = false;
}
二、SQL
<select id="selectTreeNodes" resultType="com.dkchy.domain.entity.sys.SysOrg">
WITH RECURSIVE org_tree AS (
SELECT
*
FROM sys_org
WHERE id = #{orgId}
UNION ALL
SELECT
t.*
FROM sys_org t
INNER JOIN org_tree ot ON t.parent_id = ot.id
<!-- 新增:name 模糊查询条件 -->
<if test="name != null and name != ''">
WHERE t.name LIKE #{name}
</if>
)
SELECT * FROM org_tree
</select>
三、组装为树型
/**
* 获取用户所在行政区划树(当前层级以及所有下级)
*
* @param req
* @return
*/
@Override
public Result<SysOrg> orgTree(SysOrgPageReq req, LoginUserVO user) {
if (ObjectUtils.isEmpty(req.getOrgId())) {
Long userOrgId = sysOrgUserService.getUserOrgId(user.getUserId());
if (ObjectUtils.isEmpty(userOrgId)) {
return Result.paramError("当前用户所在单位未知");
}
SysOrg sysOrg = getById(userOrgId);
if (ObjectUtils.isEmpty(sysOrg)) {
return Result.paramError("当前用户所在单位不存在");
}
req.setOrgId(sysOrg.getId());
}
//TODO 实现查询组织树
// 1. 构建 name 模糊查询条件
String namePattern = null;
if (StringUtils.isNotBlank(req.getName())) {
namePattern = "%" + req.getName() + "%";
}
// 2. 查询组织树(递归查询 + name 模糊查询)
List<SysOrg> allNodes = baseMapper.selectTreeNodes(req.getOrgId(), namePattern);
if (CollectionUtils.isEmpty(allNodes)) {
return Result.success(new SysOrg());
}
// 3. 构建树形结构(使用Map高效组装)
Map<Long, SysOrg> nodeMap = new HashMap<>();
Map<Long, List<SysOrg>> childrenMap = new HashMap<>();
// 3.1 建立节点索引和子节点映射
for (SysOrg node : allNodes) {
nodeMap.put(node.getId(), node);
childrenMap.computeIfAbsent(node.getParentId(), k -> new ArrayList<>()).add(node);
}
// 3.2 为每个节点设置children和hasChild
for (SysOrg node : allNodes) {
List<SysOrg> children = childrenMap.getOrDefault(node.getId(), Collections.emptyList());
node.setChildren(children);
node.setHasChild(!children.isEmpty());
}
// 获取根节点(必须包含在结果中)
SysOrg root = nodeMap.get(req.getOrgId());
if (root == null) {
return Result.paramError(req.getOrgId() + "根节点不存在");
}
return Result.success(root);
}
四、测试
@SpringBootTest(classes = {DkNjDigitalStatistic.class})
@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("dev")
@Slf4j
public class OrgTest {
@Resource
private ISysOrgService sysOrgService;
//@Test
public void test001() {
SysOrgPageReq req = new SysOrgPageReq();
//req.setOrgId(530000L);
//req.setOrgId(2014223650349527041L);
req.setOrgId(2014223940687638530L);
Result<SysOrg> orged = sysOrgService.orgTree(req, null);
System.out.println("orged = " + orged);
System.out.println("orged = " + orged);
System.out.println("orged = " + orged);
System.out.println("orged = " + orged);
}
}
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 软件从业者Hort
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果

