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

如何通过Criteria API将关联实体属性集合转为DTO的List<String>

问题解答

是否需要关联查询ServicePrefixEntity?

必须关联查询。因为ServiceMobileData的prefixes字段依赖ServicePrefixEntity的validationPrefix属性,该属性属于关联实体,无法直接从ServiceEntity中提取,所以必须通过join关联ServicePrefixEntity才能获取目标数据。

如何在multiselect中处理并存入集合?

有两种实用实现方式,可根据场景选择:

方式1:数据库端聚合(直接通过multiselect构造DTO)

借助数据库聚合函数将validationPrefix聚合为集合,再通过multiselect传入DTO构造函数。注意不同数据库的聚合函数语法有差异:

  • PostgreSQL 使用array_agg
  • MySQL 使用group_concat(需在DTO构造函数中分割为List)

示例代码(PostgreSQL环境):

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<ServiceMobileData> query = cb.createQuery(ServiceMobileData.class);
Root<ServiceEntity> serviceRoot = query.from(ServiceEntity.class);
// 左连接关联,避免过滤掉无prefix的ServiceEntity
Join<ServiceEntity, ServicePrefixEntity> prefixJoin = serviceRoot.join("servicePrefixes", JoinType.LEFT);

// 按ServiceEntity主键分组,消除一对多关联产生的笛卡尔积重复数据
query.groupBy(serviceRoot.get("id"), serviceRoot.get("name"));

// 构造DTO,将聚合后的validationPrefix集合传入
query.multiselect(
    cb.construct(
        ServiceMobileData.class,
        serviceRoot.get("id"),
        serviceRoot.get("name"),
        cb.function("array_agg", List.class, prefixJoin.get("validationPrefix"))
    )
);

List<ServiceMobileData> result = entityManager.createQuery(query).getResultList();

方式2:查询实体后内存转换(兼容性更强)

先查询ServiceEntity并抓取关联的ServicePrefixEntity集合,再在内存中转换为DTO。这种方式不依赖数据库特性,跨数据库兼容性更好:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<ServiceEntity> query = cb.createQuery(ServiceEntity.class);
Root<ServiceEntity> serviceRoot = query.from(ServiceEntity.class);
// 抓取关联集合,避免N+1查询
serviceRoot.fetch("servicePrefixes", JoinType.LEFT);
// 去重,消除一对多关联产生的重复实体
query.distinct(true);

List<ServiceEntity> entities = entityManager.createQuery(query).getResultList();
// 转换为DTO
List<ServiceMobileData> dtos = entities.stream()
    .map(entity -> new ServiceMobileData(
        entity.getId(),
        entity.getName(),
        entity.getServicePrefixes().stream()
            .map(ServicePrefixEntity::getValidationPrefix)
            .collect(Collectors.toList())
    ))
    .collect(Collectors.toList());

是否可行?

两种方式均完全可行:

  • 方式1适合需要直接通过数据库查询构造DTO、减少内存处理的场景,但需适配不同数据库的聚合函数;
  • 方式2兼容性更强,无需依赖数据库特性,适合跨数据库部署的场景,仅需在内存中做简单转换。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 01:50:33