You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Java中实现树形Person结构的ID列表过滤?

需求:基于ID列表过滤树形结构,保留目标节点及所有父节点

已实现Person实体类的树形结构生成接口,现需添加过滤功能:传入ID列表后,返回的树形仅包含列表中ID对应的节点,以及它们的所有关联父节点。


未过滤响应

[
    {
        "id": 1,
        "fullName": "William",
        "children": [
            {
                "id": 2,
                "fullName": "Henry",
                "children": [
                    {
                        "id": 3,
                        "fullName": "Matt",
                        "children": [
                            {
                                "id": 7,
                                "fullName": "Sophi",
                                "children": [],
                                "rootParentId": 1
                            }
                        ],
                        "rootParentId": 1
                    },
                    {
                        "id": 4,
                        "fullName": "Alisa",
                        "children": [
                            {
                                "id": 6,
                                "fullName": "Alexa",
                                "children": [],
                                "rootParentId": 1
                            }
                        ],
                        "rootParentId": 1
                    }
                ],
                "rootParentId": 1
            },
            {
                "id": 5,
                "fullName": "May",
                "children": [],
                "rootParentId": 1
            }
        ],
        "rootParentId": 1
    },
    {
        "id": 8,
        "fullName": "Olivia",
        "children": [
            {
                "id": 9,
                "fullName": "John",
                "children": [
                    {
                        "id": 11,
                        "fullName": "Oliver",
                        "children": [],
                        "rootParentId": 8
                    },
                    {
                        "id": 12,
                        "fullName": "Mia",
                        "children": [],
                        "rootParentId": 8
                    }
                ],
                "rootParentId": 8
            },
            {
                "id": 10,
                "fullName": "Mary",
                "children": [
                    {
                        "id": 13,
                        "fullName": "Evelyn",
                        "children": [],
                        "rootParentId": 8
                    }
                ],
                "rootParentId": 8
            }
        ],
        "rootParentId": 8
    },
    {
        "id": 14,
        "fullName": "Eric",
        "children": [],
        "rootParentId": null
    }
]

过滤ID列表

filterList = [7, 12]

预期过滤响应

[
    {
        "id": 1,
        "fullName": "William",
        "children": [
            {
                "id": 2,
                "fullName": "Henry",
                "children": [
                    {
                        "id": 3,
                        "fullName": "Matt",
                        "children": [
                            {
                                "id": 7,
                                "fullName": "Sophi",
                                "children": [],
                                "rootParentId": 1
                            }
                        ],
                        "rootParentId": 1
                    }                    
                ],
                "rootParentId": 1
            }
        ],
        "rootParentId": 1
    },
    {
        "id": 8,
        "fullName": "Olivia",
        "children": [
            {
                "id": 9,
                "fullName": "John",
                "children": [
                    {
                        "id": 12,
                        "fullName": "Mia",
                        "children": [],
                        "rootParentId": 8
                    }
                ],
                "rootParentId": 8
            }
        ],
        "rootParentId": 8
    }
]

现有代码

实体类(Person)

@Entity
@JsonIgnoreProperties({"hibernate_lazy_initializer", "handler"})
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Person {

    @Id
    @Getter
    @Setter
    @EqualsAndHashCode.Include
    private Long id;

    @Getter
    @Setter
    private String fullName;

    @ManyToOne(fetch = FetchType.LAZY)
    @Getter
    @Setter
    @JsonIgnore
    private Person parent;

    @ManyToOne(fetch = FetchType.LAZY)
    @Getter
    @Setter
    @JsonIgnore
    private Person rootParent;

    @Transient
    @Getter
    @Setter
    public List<Person> children = new ArrayList<>();

    public Long getRootParentId() {
        if (parent != null) {
            return parent.getRootParentId();
        } else if (children != null && !children.isEmpty()) {
            return id;
        } else return null;
    }

}

仓库接口(PersonRepo)

@Repository
public interface PersonRepo extends JpaRepository<Person, Long> {

    @Query("SELECT p FROM Person p "
            + " WHERE p.parent.id IS NULL")
    List<Person> findRoots();

    @Query("SELECT p FROM Person p"
            + " WHERE p.rootParent.id IN :rootIds ")
    List<Person> findChildrenInRoots(@Param("rootIds") List<Long> rootIds);

}

控制器(PersonController)

@RestController
@RequestMapping("/api/v1/person")
public class PersonController {

    @Autowired
    private PersonRepo personRepo;
    
    @GetMapping("/tree")
    @Transactional(readOnly = true)
    public List<Person> getChildren() {
        List<Person> rootCategories = personRepo.findRoots();

        List<Long> rootCategoryIds = rootCategories.stream().map(Person::getId).collect(Collectors.toList());

        List<Person> children = personRepo.findChildrenInRoots(rootCategoryIds);

        children.forEach(subCategory -> {
            subCategory.getParent().getChildren().add(subCategory);
        });

        return rootCategories;
    }
}

数据插入语句

insert into PERSON values (1,'William',null, null);
insert into PERSON values (2,'Henry',1,1);
insert into PERSON values (3,'Matt',2,1);
insert into PERSON values (4,'Alisa',2,1);
insert into PERSON values (5,'May',1,1);
insert into PERSON values (6,'Alexa',4,1);
insert into PERSON values (7,'Sophi',3,1);
insert into PERSON values (8,'Olivia',null,null);
insert into PERSON values (9,'John',8,8);
insert into PERSON values (10,'Mary',8,8);
insert into PERSON values (11,'Oliver',9,8);
insert into PERSON values (12,'Mia',9,8);
insert into PERSON values (13,'Evelyn',10,8);
insert into PERSON values (14,'Eric',null,null);

实现方案

1. 修改控制器逻辑,添加过滤参数与处理逻辑

改造原接口,接收过滤ID列表,收集目标节点及所有祖先节点的ID,再重新构建符合要求的树形结构:

@RestController
@RequestMapping("/api/v1/person")
public class PersonController {

    @Autowired
    private PersonRepo personRepo;
    
    @GetMapping("/tree")
    @Transactional(readOnly = true)
    public List<Person> getChildren(@RequestParam(required = false) List<Long> filterIds) {
        Set<Long> requiredIds = new HashSet<>();
        List<Person> allPersons;

        // 处理过滤逻辑:收集目标节点及所有祖先ID
        if (filterIds != null && !filterIds.isEmpty()) {
            for (Long targetId : filterIds) {
                Person target = personRepo.findById(targetId).orElse(null);
                if (target == null) continue;
                
                requiredIds.add(targetId);
                // 向上遍历收集所有父节点ID
                Person parent = target.getParent();
                while (parent != null) {
                    requiredIds.add(parent.getId());
                    parent = parent.getParent();
                }
            }
            // 查询所有需要保留的节点
            allPersons = personRepo.findAllById(requiredIds);
        } else {
            // 原逻辑:返回完整树形
            List<Person> roots = personRepo.findRoots();
            List<Long> rootIds = roots.stream().map(Person::getId).collect(Collectors.toList());
            allPersons = new ArrayList<>(roots);
            allPersons.addAll(personRepo.findChildrenInRoots(rootIds));
        }

        // 构建节点映射,重新组装树形
        Map<Long, Person> personMap = allPersons.stream()
                .peek(p -> p.setChildren(new ArrayList<>()))
                .collect(Collectors.toMap(Person::getId, p -> p));

        List<Person> resultRoots = new ArrayList<>();
        for (Person person : allPersons) {
            if (person.getParent() == null) {
                resultRoots.add(person);
            } else {
                Person parent = personMap.get(person.getParent().getId());
                if (parent != null) {
                    parent.getChildren().add(person);
                }
            }
        }

        // 递归过滤子节点,确保仅保留需要的节点
        filterChildren(resultRoots, requiredIds);

        return resultRoots;
    }

    // 递归过滤子节点
    private void filterChildren(List<Person> persons, Set<Long> requiredIds) {
        Iterator<Person> iterator = persons.iterator();
        while (iterator.hasNext()) {
            Person person = iterator.next();
            if (!requiredIds.contains(person.getId())) {
                iterator.remove();
            } else {
                filterChildren(person.getChildren(), requiredIds);
            }
        }
    }
}

2. 优化实体类getRootParentId方法

利用已存储的rootParent字段简化逻辑,避免递归调用:

public Long getRootParentId() {
    if (rootParent != null) {
        return rootParent.getId();
    }
    // 根节点的情况,返回自身ID
    return parent == null ? id : null;
}

3. 性能优化可选方案

如果数据量较大,可在PersonRepo中添加原生递归SQL查询,一次性获取所有祖先节点,避免循环查询数据库:

@Query(value = "WITH RECURSIVE ancestor AS (" +
               "SELECT id, parent_id FROM person WHERE id = :nodeId " +
               "UNION ALL " +
               "SELECT p.id, p.parent_id FROM person p JOIN ancestor a ON p.id = a.parent_id " +
               ") SELECT * FROM ancestor", nativeQuery = true)
List<Person> findAllAncestors(@Param("nodeId") Long nodeId);

内容的提问来源于stack exchange,提问作者Cleiton Freitas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 14:54:54