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

Spring Data JPA:如何按子实体筛选并返回含选中子实体的父实体

解决方案

1. 先对查询结果按城市分组

你当前拿到的是List<SchoolEntity>,其中可能有多个学校属于同一个城市,首先需要把这些学校按所属城市分组,避免后续出现重复的城市条目:

@GetMapping("/from-school")
public List<City> fromSchool2() {
    List<SchoolEntity> schoolEntities = schoolEntityRepository.findByNameContaining("Java");
    
    // 按所属城市分组,得到 城市 -> 该城市下符合条件的学校列表
    Map<CityEntity, List<SchoolEntity>> cityToSchoolsMap = schoolEntities.stream()
            .collect(Collectors.groupingBy(SchoolEntity::getCity));
    
    // 处理每个城市,替换原有的schools列表为筛选后的列表
    List<CityEntity> processedCities = new ArrayList<>();
    for (Map.Entry<CityEntity, List<SchoolEntity>> entry : cityToSchoolsMap.entrySet()) {
        CityEntity city = entry.getKey();
        // 替换原实体的全量schools为筛选后的列表
        city.setSchools(entry.getValue());
        processedCities.add(city);
    }
    
    // 用MapStruct映射成City DTO
    return CityEntityMapper.INSTANCE.map(processedCities);
}

2. 调整MapStruct映射器

你的原MapStruct接口只支持从List<SchoolEntity>映射,现在需要改成支持从List<CityEntity>映射到List<City>,同时处理嵌套的School DTO:

先补全你的DTO定义(假设未完成):

// City DTO
public class City {
    private int id;
    private String name;
    private String postcode;
    private List<School> schools;
    // getter/setter
}

// School DTO
public class School {
    private int id;
    private String name;
    // getter/setter
}

修改MapStruct接口:

@Mapper
public interface CityEntityMapper {
    CityEntityMapper INSTANCE = Mappers.getMapper(CityEntityMapper.class);
    
    // 单个CityEntity转City
    City map(CityEntity cityEntity);
    
    // 批量转换CityEntity列表
    List<City> map(List<CityEntity> cityEntities);
    
    // 单个SchoolEntity转School
    School map(SchoolEntity schoolEntity);
    
    // 批量转换SchoolEntity列表
    List<School> map(List<SchoolEntity> schoolEntities);
}

3. 优化:直接从CityEntity查询(更高效)

上面的方法需要先查学校再分组,其实可以直接用JPQL从CityEntity入手,关联筛选符合条件的学校,一步拿到结果,避免内存分组和N+1查询问题:

在CityEntityRepository中添加查询方法:

public interface CityEntityRepository extends JpaRepository<CityEntity, Integer> {
    @Query("SELECT c FROM CityEntity c JOIN FETCH c.schools s WHERE s.name LIKE %:name%")
    List<CityEntity> findBySchoolsNameContaining(@Param("name") String name);
}

控制器直接调用:

@GetMapping("/from-school")
public List<City> fromSchool2() {
    List<CityEntity> cityEntities = cityEntityRepository.findBySchoolsNameContaining("Java");
    return CityEntityMapper.INSTANCE.map(cityEntities);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 08:52:54