Java Stream过滤嵌套对象:查找含指定国家资源的Release
问题背景
- 涉及3个JPA实体:
projects、releases、resources,实体关联关系如下:project与releases为一对多关联release与resources通过track实体做关联,为一对多关系
- 核心需求:查询指定project下,至少包含1个归属指定国家的resource的所有release
原有实现的问题
原有流式处理代码如下:
project.getProjectReleases().stream() .map(ProjectRelease::getRelease) .flatMap(release -> release.getTracks().stream()) .map(Track::getResource) .filter(resource -> Country.US.name().equals(resource.getCountry())) .flatMap(resource -> resource.getTracks().stream()) .map(Track::getRelease) .collect(Collectors.toList());
该逻辑存在明确缺陷:
- 结果重复:如果同一个release下有多个符合国家条件的resource,该release会被多次收集到结果集中
- 逻辑冗余:不需要从匹配到的resource反向回溯关联release,绕路处理也增加了空指针风险
- 性能浪费:会遍历所有关联的track、resource实体,没有在匹配到符合条件的resource时提前终止判断
优化实现方案
根据数据加载场景二选一即可:
方案1:内存流修正(适合已加载全量关联数据、数据量小的场景)
直接在release维度做存在性判断,匹配到符合条件的resource就直接判定该release符合要求,同时做去重处理:
List<Release> result = project.getProjectReleases().stream() .map(ProjectRelease::getRelease) .filter(release -> release.getTracks().stream() .map(Track::getResource) // 只要存在任意一个符合国家条件的resource,就判定当前release匹配,直接终止当前release的遍历 .anyMatch(res -> Country.US.name().equals(res.getCountry())) ) .distinct() .collect(Collectors.toList());
方案2:JPQL数据库层查询(推荐,适合数据量较大的场景)
无需把所有关联数据加载到内存再过滤,直接通过JPA查询在数据库层完成关联过滤,性能提升明显:
// 定义在Release对应的Repository中 @Query("select distinct r from Project p " + "join p.projectReleases pr " + "join pr.release r " + "join r.tracks t " + "join t.resource res " + "where p.id = :projectId and res.country = :targetCountry") List<Release> queryMatchedReleases(@Param("projectId") Long projectId, @Param("targetCountry") String targetCountry); // 业务层调用 List<Release> result = releaseRepository.queryMatchedReleases(project.getId(), Country.US.name());
内容的提问来源于stack exchange,提问作者Carlos Gonzalez
相关产品推荐
相关产品推荐

