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

如何将Flux中的EventCategoryEntity聚合映射为指定DTO结构?

问题描述

我定义了如下实体类EventCategoryEntity:

public class EventCategoryEntity {
    private UUID id;

    @Column("category_name")
    private String categoryName;

    @Column("group_name")
    private String groupName;
}

通过Flux(一对多关系)查询获取数据:

public Flux<EventCategoryEntity> findAllCategories() {
    return eventRepository.findAll();
}

@Query("select ec.id, ec.name as category_name,ecg.name as group_name" +
        "from event_category ec left join event_category_relation ecr on (ec.id = ecr.event_category_id) " +
        "left join event_category_group ecg on (ecr.event_category_group_id = ecg.id);")
Flux<EventCategoryEntity> findAll();

当前Controller返回的JSON响应如下:

[
    {
        "id": "87108493-4fc1-4b12-8ffc-e10aa039fc39",
        "categoryName": "soccer",
        "groupName": "team"
    },
    {
        "id": "87108493-4fc1-4b12-8ffc-e10aa039fc39",
        "categoryName": "soccer",
        "groupName": "ball"
    }
]

但我希望按id聚合后的响应结构如下:

[
    {
        "id": "87108493-4fc1-4b12-8ffc-e10aa039fc39",
        "categoryName": "soccer",
        "groupName": ["team", "ball"]
    }
]

我已准备好DTO类:

public class EventCategory {
    private UUID id;

    private String categoryName;

    private List<CategoryGroup> categoryGroup;

    private class CategoryGroup {
        private String groupName;
    }
}

但不知如何将Flux中的数据映射转换为该DTO结构。

解决方案

可以利用Reactor的操作符对Flux<EventCategoryEntity>进行分组、聚合,再映射到目标DTO,具体步骤如下:

1. 按id分组

使用groupBy操作符,将相同id的EventCategoryEntity归为一组:

Flux<GroupedFlux<UUID, EventCategoryEntity>> groupedFlux = eventRepository.findAll()
    .groupBy(EventCategoryEntity::getId);

2. 对分组数据聚合转换

对每个分组,提取共同的id和categoryName,收集所有groupName并构建DTO:

Flux<EventCategory> resultFlux = groupedFlux.flatMap(group -> {
    // 取分组内第一个元素的基础信息(同组内id、categoryName一致)
    return group.take(1)
        .flatMap(firstEntity -> {
            // 收集当前分组所有的groupName
            return group.map(EventCategoryEntity::getGroupName)
                .collectList()
                .map(groupNames -> {
                    // 转换为CategoryGroup列表
                    List<EventCategory.CategoryGroup> categoryGroups = groupNames.stream()
                        .map(name -> {
                            EventCategory.CategoryGroup group = new EventCategory().new CategoryGroup();
                            group.setGroupName(name);
                            return group;
                        })
                        .collect(Collectors.toList());
                    
                    // 构建最终DTO对象
                    EventCategory dto = new EventCategory();
                    dto.setId(firstEntity.getId());
                    dto.setCategoryName(firstEntity.getCategoryName());
                    dto.setCategoryGroup(categoryGroups);
                    return dto;
                });
        });
});

3. 关键说明

  • 因为同组数据的id和categoryName完全一致,用take(1)获取基础信息能避免重复处理,提升效率;
  • 若允许调整DTO结构,把categoryGroup改为List<String>,可省去CategoryGroup的转换步骤,直接赋值groupNames;
  • 非静态内部类CategoryGroup需要通过外部类实例创建,即new EventCategory().new CategoryGroup()。

4. Controller返回处理结果

在Controller方法中直接返回上述resultFlux,Spring WebFlux会自动序列化为期望的JSON结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 12:36:18